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..ae8c7aef11bba6c98adc54f5f9899f3255125196 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,21 @@ [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 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..f8e28a3d23e7b0ada5040a5ee01fb8d13d855962 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,40 @@ 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) + +# 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 +43,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 +72,57 @@ 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() 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 +130,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..10af74566eafdaec7c78e8ab5a22a2b3faf1bcc6 100644 --- a/README.md +++ b/README.md @@ -11,17 +11,51 @@ 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`. -## Documentation +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) + +### Dependencies (required): + +Most dependencies will be 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 documentation for the develop-branch can be found here: -https://vkcv.de/develop/ +The following modules will be provided in this repository and they will automatically be builded 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) + +## 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..fb4677e6e13698dcae87657290463253f40d1c1e 100644 --- a/config/Libraries.cmake +++ b/config/Libraries.cmake @@ -3,13 +3,23 @@ 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) + 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 +29,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 +44,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 54bb3485ed975669668d987787975f019aa6358b..db4b200fa99597af4f96432e35e80af387ae881a 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,13 +27,19 @@ 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/Image.hpp @@ -30,57 +48,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/ImageConfig.hpp - ${vkcv_source}/vkcv/ImageConfig.cpp + ${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..9270dbd0345660802487b0b1fdfd0f769f4ce958 --- /dev/null +++ b/config/lib/VulkanMemoryAllocator.cmake @@ -0,0 +1,58 @@ + +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-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..2377e371e71e212b8a1142be1f2f1ea732f2d9ab --- /dev/null +++ b/config/lib/vma/CMakeLists.txt @@ -0,0 +1,69 @@ +cmake_minimum_required(VERSION 3.9) + +project(VulkanMemoryAllocator) + +find_package(Vulkan REQUIRED) + +option(VMA_VULKAN_VERSION "Enforced Vulkan version" OFF) +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 + # Use C++14 + CXX_STANDARD 14 + CXX_STANDARD_REQUIRED ON +) + +target_include_directories(VulkanMemoryAllocator SYSTEM BEFORE PRIVATE ${VMA_VULKAN_H_PATH} ${VMA_VULKAN_HPP_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..307c27f096bd1bae2b1deb2ca5994f132adc92cc --- /dev/null +++ b/config/lib/vma/vma.cpp @@ -0,0 +1,58 @@ + +#ifndef NDEBUG +#define _DEBUG +#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 cbbe1e908cdb74891ab9bfe4416c03e487e76b26..1a4ce7c8ca203661d6bb21fdfbde0beb8ac02b12 100644 --- a/include/vkcv/Core.hpp +++ b/include/vkcv/Core.hpp @@ -1,297 +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 "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, width, height, depth + * and a lot more optional parameters. + * + * @param[in] format Image format + * @param[in] width Image width + * @param[in] height Image height + * @param[in] depth Image depth + * @param[in] createMipChain Flag to create a mip chain + * @param[in] supportStorage Flag whether support storage + * @param[in] supportColorAttachment Flag whether attachment is supported + * @param[in] multisampling Multisampling + * @return Image handle + */ + [[nodiscard]] ImageHandle createImage(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); + + /** + * @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 + */ + void fillImage(const ImageHandle &image, const void* data, size_t size); + + /** + * @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; - - 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, - Multisampling multisampling = Multisampling::None); - - [[nodiscard]] - const uint32_t getImageWidth(ImageHandle imageHandle); - [[nodiscard]] - const uint32_t getImageHeight(ImageHandle imageHandle); - - /** 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); - void resolveMSAAImage(CommandStreamHandle cmdStream, ImageHandle src, ImageHandle dst); - - 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..cfadbde29b31edf0fc5c2df1e5dcbff0cdf897f2 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, 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, 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, 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, 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, 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..1617c721fa0976711f9e553ae3214222999c516c --- /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); + + ~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 9f162a499a38d5633703f70eec8a8682e3328d72..0000000000000000000000000000000000000000 --- a/include/vkcv/DrawcallRecording.hpp +++ /dev/null @@ -1,55 +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, const uint32_t instanceCount = 1) - : mesh(mesh), descriptorSets(descriptorSets), instanceCount(instanceCount){} - - Mesh mesh; - std::vector<DescriptorSetUsage> descriptorSets; - uint32_t instanceCount; - }; - - 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..f2138f6f3b7c5f639d42e98a83991704df0eb352 --- /dev/null +++ b/include/vkcv/FeatureManager.hpp @@ -0,0 +1,501 @@ +#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::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 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 85ab2b81e2718b3890ba361c988d5db0e40e84c7..69d4bc07e7f40af3d5d2802dd06576f166329427 100644 --- a/include/vkcv/Image.hpp +++ b/include/vkcv/Image.hpp @@ -1,65 +1,135 @@ #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 "vkcv/ImageConfig.hpp" +#include "Multisampling.hpp" namespace vkcv { - - 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; - - [[nodiscard]] - vkcv::ImageHandle getHandle() const; - - [[nodiscard]] - uint32_t getMipCount() 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; + + /** + * @brief Returns the width of the image. + * + * @return Width of the image + */ + [[nodiscard]] uint32_t getWidth() 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, - Multisampling msaa); + /** + * @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 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); + +} // namespace vkcv diff --git a/include/vkcv/ImageConfig.hpp b/include/vkcv/ImageConfig.hpp deleted file mode 100644 index 2e413b97be92ae771ef85342981ea0163a93ab52..0000000000000000000000000000000000000000 --- a/include/vkcv/ImageConfig.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include <vulkan/vulkan.hpp> - -namespace vkcv { - enum class Multisampling { None, MSAA2X, MSAA4X, MSAA8X }; - - vk::SampleCountFlagBits msaaToVkSampleCountFlag(Multisampling msaa); - uint32_t msaaToSampleCount(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 f3b2b802d062a441dfb0c810154205effb7053a2..efe741d07fd731a2c71fd6bf706cda7e39d43a50 100644 --- a/include/vkcv/PassConfig.hpp +++ b/include/vkcv/PassConfig.hpp @@ -1,53 +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> -#include "ImageConfig.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, Multisampling msaa = Multisampling::None) noexcept; - std::vector<AttachmentDescription> attachments{}; - Multisampling msaa; - }; -} \ 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 5e6dbaa3306f8d2aa6fc44d7dd1fadd9b79be3b4..575f17057ed1f0a8588fcf141a11a0e539aca178 100644 --- a/include/vkcv/PipelineConfig.hpp +++ b/include/vkcv/PipelineConfig.hpp @@ -1,44 +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" -#include "ImageConfig.hpp" namespace vkcv { - enum class PrimitiveTopology{PointList, LineList, TriangleList }; - enum class CullMode{ None, Front, Back }; - enum class DepthTest { None, Less, LessEqual, Greater, GreatherEqual, Equal }; - - // add more as needed - // alternatively we could expose the blend factors directly - enum class BlendMode{ None, Additive }; - - 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; - BlendMode m_blendMode = BlendMode::None; - bool m_EnableDepthClamping = false; - Multisampling m_multisampling = Multisampling::None; - CullMode m_culling = CullMode::None; - DepthTest m_depthTest = DepthTest::LessEqual; - bool m_depthWrite = true; - bool m_alphaToCoverage = false; - }; - -} \ 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..7ba083f698bffdaf17179c0d25891aba4ef881a2 --- /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(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(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 5e9bc7d0593a3b2e1f1f8e7b5ef7ea69e9711fb5..0000000000000000000000000000000000000000 --- a/include/vkcv/Swapchain.hpp +++ /dev/null @@ -1,126 +0,0 @@ -#pragma once -#include "vulkan/vulkan.hpp" -#include "Context.hpp" -#include "vkcv/Window.hpp" - -#include <atomic> - -namespace vkcv -{ - - const uint32_t MIN_SWAPCHAIN_SIZE = 2; - - 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..4ee015db3d0765bc667ef7caf74cf5b2d64a6f45 --- /dev/null +++ b/include/vkcv/VertexData.hpp @@ -0,0 +1,67 @@ +#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 + }; + + class VertexData { + private: + VertexBufferBindings m_bindings; + BufferHandle m_indices; + IndexBitCount m_indexBitCount; + size_t m_count; + + public: + 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; + + [[nodiscard]] const VertexBufferBindings &getVertexBufferBindings() const; + + void setIndexBuffer(const BufferHandle &indices, + IndexBitCount indexBitCount = IndexBitCount::Bit16); + + [[nodiscard]] const BufferHandle &getIndexBuffer() const; + + [[nodiscard]] IndexBitCount getIndexBitCount() const; + + void setCount(size_t count); + + [[nodiscard]] size_t getCount() const; + }; + +} // namespace vkcv diff --git a/include/vkcv/VertexLayout.hpp b/include/vkcv/VertexLayout.hpp index 0600b99a24a327605e89b2e8ec304c20dbf7ad2e..88638261483e870418a4e7af86424e6798b1c1e7 100644 --- a/include/vkcv/VertexLayout.hpp +++ b/include/vkcv/VertexLayout.hpp @@ -1,66 +1,100 @@ #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; + }; - std::vector<VertexBinding> vertexBindings; - }; -} +} // namespace vkcv diff --git a/include/vkcv/Window.hpp b/include/vkcv/Window.hpp index f3b3a8fe88ae6e8791d7d92361ad5b6bf2447dcb..3d157b98220f17752d6b109401c3d21899299f18 100644 --- a/include/vkcv/Window.hpp +++ b/include/vkcv/Window.hpp @@ -1,169 +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(); - - /** - * gets the windows framebuffer size - * @param width - * @param height - */ - void getFramebufferSize(int& width, int& height) const; - }; - -} + /** + * @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..6a67891418a3f08be63f92726e049dc788e46f5b 160000 --- a/lib/SPIRV-Cross +++ b/lib/SPIRV-Cross @@ -1 +1 @@ -Subproject commit ff61890722a91e97c44940494be5b6eed0d5ff5b +Subproject commit 6a67891418a3f08be63f92726e049dc788e46f5b 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..ae1b0c36df0943795cd621a37e7f7bfd00ac958a --- /dev/null +++ b/lib/Vulkan-Hpp @@ -0,0 +1 @@ +Subproject commit ae1b0c36df0943795cd621a37e7f7bfd00ac958a diff --git a/lib/VulkanMemoryAllocator-Hpp b/lib/VulkanMemoryAllocator-Hpp new file mode 160000 index 0000000000000000000000000000000000000000..2e6f3f6c13933c84b55d01c4c0cbc2e7385ac8c1 --- /dev/null +++ b/lib/VulkanMemoryAllocator-Hpp @@ -0,0 +1 @@ +Subproject commit 2e6f3f6c13933c84b55d01c4c0cbc2e7385ac8c1 diff --git a/lib/glfw b/lib/glfw index 0e9ec7788b4985a0df698080258e4091d18dcc3b..4cb36872a5fe448c205d0b46f0e8c8b57530cfe0 160000 --- a/lib/glfw +++ b/lib/glfw @@ -1 +1 @@ -Subproject commit 0e9ec7788b4985a0df698080258e4091d18dcc3b +Subproject commit 4cb36872a5fe448c205d0b46f0e8c8b57530cfe0 diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt index 28b2184b2a83515a514f1428733bcf8cf1499633..e5bc34dfdf183f8dc26639771f44201597beaa73 100644 --- a/modules/CMakeLists.txt +++ b/modules/CMakeLists.txt @@ -1,8 +1,26 @@ +set(vkcv_modules_includes) +set(vkcv_modules_libraries) + # Add new modules here: +add_subdirectory(algorithm) add_subdirectory(asset_loader) -add_subdirectory(material) 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..eee9f361493c35c5214ac661448f3cc80384e4ee --- /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, { + vkcv::DescriptorSetUsage(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 471870fb1e5af3d3c448a66611d9754db9597f85..d6b5cf5d5382e0e03bd7950c1e87bf3c6a8ee445 100644 --- a/modules/asset_loader/include/vkcv/asset/asset_loader.hpp +++ b/modules/asset_loader/include/vkcv/asset/asset_loader.hpp @@ -11,17 +11,9 @@ #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. * @@ -46,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: @@ -103,100 +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, - TANGENT = 5 + 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; +}; /** - * Load every mesh from the glTF file, as well as materials and textures. + * 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 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 + * 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, textures and other + * associated objects. + * + * @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); - -struct TextureData { - int width; - int height; - int componentCount; - std::vector<char*> data; -}; -TextureData loadTexture(const std::filesystem::path& path); + * @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..4f8fba14066156b73f1189a2b8bd568bde5284c5 160000 --- a/modules/asset_loader/lib/json +++ b/modules/asset_loader/lib/json @@ -1 +1 @@ -Subproject commit 0972f7ff0e651f09a306dba791cc42024b8642c1 +Subproject commit 4f8fba14066156b73f1189a2b8bd568bde5284c5 diff --git a/modules/asset_loader/lib/stb b/modules/asset_loader/lib/stb index c9064e317699d2e495f36ba4f9ac037e88ee371a..af1a5bc352164740c1cc1354942b1c6b72eacb8a 160000 --- a/modules/asset_loader/lib/stb +++ b/modules/asset_loader/lib/stb @@ -1 +1 @@ -Subproject commit c9064e317699d2e495f36ba4f9ac037e88ee371a +Subproject commit af1a5bc352164740c1cc1354942b1c6b72eacb8a diff --git a/modules/asset_loader/src/vkcv/asset/asset_loader.cpp b/modules/asset_loader/src/vkcv/asset/asset_loader.cpp index e3d3072543bd33e1f5a67ae7dac61d229005947a..d8f2563d867e5521bce29a3880650f0418ad9db8 100644 --- a/modules/asset_loader/src/vkcv/asset/asset_loader.cpp +++ b/modules/asset_loader/src/vkcv/asset/asset_loader.cpp @@ -1,387 +1,886 @@ #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: - vkcv_log(LogLevel::ERROR, "Index type not supported: %u", static_cast<uint16_t>(t)); - 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 if (attrib.first == "TANGENT") { - attribute.type = PrimitiveType::TANGENT; - } 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; - - 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 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; + } - textures.push_back({ - 0, - static_cast<uint8_t>(c), - static_cast<uint16_t>(w), - static_cast<uint16_t>(h), - imgdata - }); + /** + * 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; + } - } - } + bool Material::hasTexture(const PBRTextureTarget target) const { + return textureMask & bitflag(target); + } - if (sceneObjects.materials.size() > 0){ - materials.reserve(sceneObjects.materials.size()); + /** + * 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; + } + } - 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] - } + /** + * 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; + } - }); - } - } + /** + * 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; + } - scene = { - meshes, - vertexGroups, - materials, - textures, - samplers - }; + /** + * 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; + } - return 1; -} + 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; + } -TextureData loadTexture(const std::filesystem::path& path) { - TextureData texture; - - uint8_t* data = stbi_load(path.string().c_str(), &texture.width, &texture.height, &texture.componentCount, 4); - - if (!data) { - vkcv_log(LogLevel::ERROR, "Texture could not be loaded from '%s'", path.c_str()); - - texture.width = 0; - texture.height = 0; - texture.componentCount = 0; - return texture; - } - - texture.data.resize(texture.width * texture.height * 4); - memcpy(texture.data.data(), data, texture.data.size()); - return texture; -} + 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); + + if (result != ASSET_SUCCESS) { + vkcv_log(LogLevel::ERROR, "Loading scene failed '%s'", + path.c_str()); + return result; + } + + for (size_t 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 60cfca4cf97cef30d989bdab064e20547764041c..dbe5c85c0ae839a0b0ab8c3de059c442d22f2d59 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) @@ -21,19 +21,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 9d85df7dce6d043630fd9d39287cace8530dbd6a..b333fbbd696a4583bd4aa9f9591a75d4de532d1d 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 @@ -75,7 +86,7 @@ namespace vkcv::camera { * @brief Gets the current projection of the camera * @return The current projection matrix */ - glm::mat4 getProjection() const; + const glm::mat4& getProjection() const; /** * @brief Gets the model-view-projection matrix of the camera with y-axis-correction applied @@ -156,6 +167,20 @@ namespace vkcv::camera { * @param[in] center The new center point. */ void setCenter(const glm::vec3& center); + + /** + * @brief Gets 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. @@ -194,4 +219,6 @@ namespace vkcv::camera { 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..d6efd8f2f300dead870b3546939dad2088cd466e 100644 --- a/modules/camera/include/vkcv/camera/CameraController.hpp +++ b/modules/camera/include/vkcv/camera/CameraController.hpp @@ -1,13 +1,25 @@ #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 { @@ -69,4 +81,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 409f9196599be02e4215f3924c1102f0b8c72899..c54ec94094d7ed13775832cd57b7aa77cf92ef80 100644 --- a/modules/camera/include/vkcv/camera/CameraManager.hpp +++ b/modules/camera/include/vkcv/camera/CameraManager.hpp @@ -1,24 +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, - TRACE - }; /** * @brief Used for managing an arbitrary amount of camera controllers. @@ -35,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; @@ -117,7 +121,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 @@ -129,26 +133,26 @@ 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 Gets 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); + Camera& getCamera(const CameraHandle& cameraHandle); /** * @brief Gets the stored camera object set as the active camera. @@ -157,34 +161,34 @@ namespace vkcv::camera { 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 Gets the handle of the stored active camera object. + * @return The active camera handle. */ - uint32_t getActiveCameraIndex() const; + 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 Gets 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); + ControllerType getControllerType(const CameraHandle& cameraHandle); /** * @brief Updates all stored camera controllers in respect to @p deltaTime. @@ -192,4 +196,7 @@ namespace vkcv::camera { */ 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..c1a5ef34bf8172956d5c9812f45b73f746c48838 --- /dev/null +++ b/modules/camera/include/vkcv/camera/ControllerType.hpp @@ -0,0 +1,28 @@ +#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, + TRACE + }; + + /** @} */ + +} diff --git a/modules/camera/include/vkcv/camera/PilotCameraController.hpp b/modules/camera/include/vkcv/camera/PilotCameraController.hpp index 2b64cdc0dd3045714aba7b3b7c6241af2337c706..c23a2cccce6f3d4036e9648d39b7b39df1d7ce4b 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: /** @@ -147,4 +122,6 @@ namespace vkcv::camera { void gamepadCallback(int gamepadIndex, Camera &camera, double frametime); }; + /** @} */ + } \ 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..e4f40383218f8c25fd0bca41d9ff673ce5a3589e 100644 --- a/modules/camera/include/vkcv/camera/TrackballCameraController.hpp +++ b/modules/camera/include/vkcv/camera/TrackballCameraController.hpp @@ -1,9 +1,20 @@ #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. */ @@ -14,6 +25,8 @@ namespace vkcv::camera { 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. @@ -104,4 +117,6 @@ namespace vkcv::camera { void gamepadCallback(int gamepadIndex, Camera &camera, double frametime); }; + /** @} */ + } \ 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 3541b1a5bc1253c6b0f2b044d757341855a5e900..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; @@ -44,20 +42,20 @@ namespace vkcv::camera { 0.0f, 0.0f, 0.0f, 1.0f ); - glm::mat4 Camera::getProjection() const { - return y_correction * m_projection; + const glm::mat4& Camera::getProjection() const { + return m_projection; } void Camera::setProjection(const glm::mat4& projection) { - m_projection = glm::inverse(y_correction) * projection; + m_projection = y_correction * projection; } glm::mat4 Camera::getMVP() const { - 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 f129f3a248325957cb56470e2547a0146bc7c971..a2ead14ce30d8088ea77ecbfd20d5af29c2c181d 100644 --- a/modules/camera/src/vkcv/camera/CameraManager.cpp +++ b/modules/camera/src/vkcv/camera/CameraManager.cpp @@ -35,8 +35,8 @@ 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));; } } } @@ -52,8 +52,8 @@ namespace vkcv::camera { } void CameraManager::mouseMoveCallback(double x, double y){ - auto xoffset = static_cast<float>(x - m_lastX); - auto yoffset = static_cast<float>(y - m_lastY); + 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()); @@ -110,67 +110,67 @@ namespace vkcv::camera { } CameraController& CameraManager::getActiveController() { - const ControllerType type = getControllerType(getActiveCameraIndex()); + const ControllerType type = getControllerType(getActiveCameraHandle()); return getControllerByType(type); } - 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) { diff --git a/modules/camera/src/vkcv/camera/PilotCameraController.cpp b/modules/camera/src/vkcv/camera/PilotCameraController.cpp index 5460858ab48d81252787b3c0141dd72982faca7d..1c7bb12679e57c9221465452f2fc41a539b6b2a0 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); } 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..8de2beb87d8f29415db611bfe0d17c5efd57a2a3 100644 --- a/modules/camera/src/vkcv/camera/TrackballCameraController.cpp +++ b/modules/camera/src/vkcv/camera/TrackballCameraController.cpp @@ -8,6 +8,8 @@ namespace vkcv::camera { m_radius = 3.0f; m_cameraSpeed = 2.5f; m_scrollSensitivity = 0.2f; + m_pitch = 0.0f; + m_yaw = 0.0f; } void TrackballCameraController::setRadius(const float radius) { @@ -21,14 +23,10 @@ namespace vkcv::camera { } // 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); + m_yaw = m_yaw + static_cast<float>(xOffset) * 90.0f * m_cameraSpeed; // 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); + m_pitch = m_pitch + static_cast<float>(yOffset) * 90.0f * m_cameraSpeed; } void TrackballCameraController::updateRadius(double offset, Camera &camera) { @@ -44,14 +42,11 @@ namespace vkcv::camera { } 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 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::mat4 rotationY = glm::rotate(glm::mat4(1.0f), glm::radians(m_yaw), yAxis); + const glm::mat4 rotationX = glm::rotate(rotationY, -glm::radians(m_pitch), xAxis); const glm::vec3 translation = glm::vec3( rotationX * glm::vec4(0.0f, 0.0f, m_radius, 0.0f) ); @@ -72,15 +67,10 @@ namespace vkcv::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,7 +86,7 @@ namespace vkcv::camera { GLFWgamepadstate gamepadState; glfwGetGamepadState(gamepadIndex, &gamepadState); - float sensitivity = 100.0f; + float sensitivity = 1.0f; double threshold = 0.1; // handle rotations 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/voxelization/resources/shaders/bloomDownsample.comp b/modules/effects/shaders/bloomDownsample.comp similarity index 100% rename from projects/voxelization/resources/shaders/bloomDownsample.comp rename to modules/effects/shaders/bloomDownsample.comp diff --git a/projects/voxelization/resources/shaders/bloomFlaresComposite.comp b/modules/effects/shaders/bloomFlaresComposite.comp similarity index 97% rename from projects/voxelization/resources/shaders/bloomFlaresComposite.comp rename to modules/effects/shaders/bloomFlaresComposite.comp index 57174b73ae3b58023d01defd26f636e13cb4709c..21d67393b634c8e639c0b81669e96070c380e6f6 100644 --- a/projects/voxelization/resources/shaders/bloomFlaresComposite.comp +++ b/modules/effects/shaders/bloomFlaresComposite.comp @@ -5,12 +5,17 @@ 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; }; @@ -52,6 +57,8 @@ float getLensDirtWeight(vec2 uv){ return mix(1, dirt, dirtStrength); } +#endif + void main() { if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(colorBuffer)))){ @@ -73,17 +80,17 @@ void main() 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; - - //composite_color.rgb = vec3(1) * starburst(UV); imageStore(colorBuffer, pixel_coord, composite_color); } \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/bloomUpsample.comp b/modules/effects/shaders/bloomUpsample.comp similarity index 100% rename from projects/voxelization/resources/shaders/bloomUpsample.comp rename to modules/effects/shaders/bloomUpsample.comp diff --git a/projects/voxelization/resources/shaders/lensFlares.comp b/modules/effects/shaders/lensFlares.comp similarity index 100% rename from projects/voxelization/resources/shaders/lensFlares.comp rename to modules/effects/shaders/lensFlares.comp diff --git a/modules/effects/src/vkcv/effects/BloomAndFlaresEffect.cpp b/modules/effects/src/vkcv/effects/BloomAndFlaresEffect.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f31596ae80c86a432f62a50621de949961e83d56 --- /dev/null +++ b/modules/effects/src/vkcv/effects/BloomAndFlaresEffect.cpp @@ -0,0 +1,548 @@ + +#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, + { DescriptorSetUsage(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, + { DescriptorSetUsage(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, + { DescriptorSetUsage(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, + { DescriptorSetUsage(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 + )); + + if ((!m_blurImage) || + (halfWidth != m_core.getImageWidth(m_blurImage)) || + (halfHeight != m_core.getImageHeight(m_blurImage))) { + m_blurImage = m_core.createImage( + m_core.getImageFormat(output), + halfWidth, + halfHeight, + 1, + true, + 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), + halfWidth, + halfHeight, + 1, + true, + 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..7924dfa570c510bddab6e0003a17ed7602290d0d --- /dev/null +++ b/modules/geometry/include/vkcv/geometry/Geometry.hpp @@ -0,0 +1,105 @@ +#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; + + 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..6ad79aae3eb5bf809c30bf1168171e9e55857e45 --- /dev/null +++ b/modules/geometry/lib/glm @@ -0,0 +1 @@ +Subproject commit 6ad79aae3eb5bf809c30bf1168171e9e55857e45 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..0256261e4bc2c4dc73bc51721718e4e986905d15 --- /dev/null +++ b/modules/geometry/src/vkcv/geometry/Cuboid.cpp @@ -0,0 +1,193 @@ + +#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 + }; + + const auto& position = getPosition(); + const auto& size = getSize(); + + for (size_t i = 0; i < 8; 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); + + VertexData data ({ + vkcv::vertexBufferBinding(positionBuffer.getHandle()), + vkcv::vertexBufferBinding(normalBuffer.getHandle()), + vkcv::vertexBufferBinding(uvBuffer.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::array<uint16_t, cuboidIndices.size()> cuboidIndices16; + + 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..cffdaf47fb44c916032dda8f62651c7c3d6962c6 --- /dev/null +++ b/modules/geometry/src/vkcv/geometry/Cylinder.cpp @@ -0,0 +1,134 @@ + +#include "vkcv/geometry/Cylinder.hpp" + +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); + 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)); + + 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 + 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; + } + + 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 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()) + }); + + 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..ccdc45fc1be7301691f1ec14e705a0f68d83fe88 --- /dev/null +++ b/modules/geometry/src/vkcv/geometry/Geometry.cpp @@ -0,0 +1,17 @@ + +#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; + } + +} diff --git a/modules/geometry/src/vkcv/geometry/Sphere.cpp b/modules/geometry/src/vkcv/geometry/Sphere.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e7ed47dc13265dd83d186ad88834950b8c08b693 --- /dev/null +++ b/modules/geometry/src/vkcv/geometry/Sphere.cpp @@ -0,0 +1,106 @@ + +#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); + } + + 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 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()) + }); + + 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..d1c7465ab646ba4ffcce0f356e5d4935b4e610e1 --- /dev/null +++ b/modules/geometry/src/vkcv/geometry/Teapot.cpp @@ -0,0 +1,14882 @@ + +#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 + }; + + 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 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()) + }); + + 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..eda7792b151d138e15df951578253b821ceed5a3 160000 --- a/modules/gui/lib/imgui +++ b/modules/gui/lib/imgui @@ -1 +1 @@ -Subproject commit d5828cd988db525f27128edeadb1a689cd2d7461 +Subproject commit eda7792b151d138e15df951578253b821ceed5a3 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 index d5b654cc6d00ce77d93b4666f48b7a5097e674b3..ee3b36e8f411f3d4d6d86a19e498f2faa0513f86 100644 --- a/modules/material/CMakeLists.txt +++ b/modules/material/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.16) project(vkcv_material) # setting c++ standard for the module -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(vkcv_material_source ${PROJECT_SOURCE_DIR}/src) @@ -12,12 +12,10 @@ set(vkcv_material_include ${PROJECT_SOURCE_DIR}/include) set(vkcv_material_sources ${vkcv_material_include}/vkcv/material/Material.hpp ${vkcv_material_source}/vkcv/material/Material.cpp - ${vkcv_material_include}/vkcv/material/PBRMaterial.hpp - ${vkcv_material_source}/vkcv/material/PBRMaterial.cpp ) # adding source files to the module -add_library(vkcv_material STATIC ${vkcv_material_sources}) +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}) @@ -27,3 +25,11 @@ target_include_directories(vkcv_material SYSTEM BEFORE PRIVATE ${vkcv_include} $ # 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 index 00b492072fa4ef8b7b41f70202d515ee4ac828fa..345d22912b368ba5bb84e96d6ce8b1dbe49e8337 100644 --- a/modules/material/include/vkcv/material/Material.hpp +++ b/modules/material/include/vkcv/material/Material.hpp @@ -1,14 +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: - const DescriptorSetHandle m_DescriptorSetHandle; - protected: - Material(const DescriptorSetHandle& setHandle); + /** + * 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/include/vkcv/material/PBRMaterial.hpp b/modules/material/include/vkcv/material/PBRMaterial.hpp deleted file mode 100644 index 09a5214b0e748a09ef8caefe5bf2b1a69ecbd8e1..0000000000000000000000000000000000000000 --- a/modules/material/include/vkcv/material/PBRMaterial.hpp +++ /dev/null @@ -1,103 +0,0 @@ -#pragma once - -#include <vector> - -#include <vkcv/DescriptorConfig.hpp> -#include <vkcv/Core.hpp> - - -#include "Material.hpp" - -namespace vkcv::material -{ - class PBRMaterial : Material - { - private: - struct vec3 { - float x, y, z; - }; - struct vec4 { - float x, y, z, a; - }; - PBRMaterial(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 DescriptorSetHandle& setHandle, - vec4 baseColorFactor, - float metallicFactor, - float roughnessFactor, - float normalScale, - float occlusionStrength, - vec3 emissiveFactor) noexcept; - - - public: - PBRMaterial() = delete; - - const ImageHandle m_ColorTexture; - const SamplerHandle m_ColorSampler; - - const ImageHandle m_NormalTexture; - const SamplerHandle m_NormalSampler; - - const ImageHandle m_MetRoughTexture; - const SamplerHandle m_MetRoughSampler; - - const ImageHandle m_OcclusionTexture; - const SamplerHandle m_OcclusionSampler; - - const ImageHandle m_EmissiveTexture; - const SamplerHandle m_EmissiveSampler; - - // - const vec4 m_BaseColorFactor; - const float m_MetallicFactor; - const float m_RoughnessFactor; - const float m_NormalScale; - const float m_OcclusionStrength; - const vec3 m_EmissiveFactor; - - /* - * Returns the material's necessary descriptor bindings which serves as its descriptor layout - * The binding is in the following order: - * 0 - diffuse texture - * 1 - diffuse sampler - * 2 - normal texture - * 3 - normal sampler - * 4 - metallic roughness texture - * 5 - metallic roughness sampler - * 6 - occlusion texture - * 7 - occlusion sampler - * 8 - emissive texture - * 9 - emissive sampler - */ - static std::vector<DescriptorBinding> getDescriptorBindings() noexcept; - - static PBRMaterial create( - vkcv::Core* core, - ImageHandle &colorImg, - SamplerHandle &colorSmp, - ImageHandle &normalImg, - SamplerHandle &normalSmp, - ImageHandle &metRoughImg, - SamplerHandle &metRoughSmp, - ImageHandle &occlusionImg, - SamplerHandle &occlusionSmp, - ImageHandle &emissiveImg, - SamplerHandle &emissiveSmp, - vec4 baseColorFactor, - float metallicFactor, - float roughnessFactor, - float normalScale, - float occlusionStrength, - vec3 emissiveFactor); - - }; -} \ No newline at end of file diff --git a/modules/material/src/vkcv/material/Material.cpp b/modules/material/src/vkcv/material/Material.cpp index 9168bcfbf924e9868ceaaff74aef5d3c6b99739c..8187b5de9205ba18ca32cd23dba9d5b457696712 100644 --- a/modules/material/src/vkcv/material/Material.cpp +++ b/modules/material/src/vkcv/material/Material.cpp @@ -1,12 +1,197 @@ #include "vkcv/material/Material.hpp" +#include <vkcv/Image.hpp> +#include <vkcv/Sampler.hpp> + namespace vkcv::material { + + Material::Material() { + m_Type = MaterialType::UNKNOWN; + } - //TODO + MaterialType Material::getType() const { + return m_Type; + } + + const DescriptorSetHandle & Material::getDescriptorSet() const { + return m_DescriptorSet; + } - Material::Material(const DescriptorSetHandle& setHandle) : m_DescriptorSetHandle(setHandle) + 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/material/src/vkcv/material/PBRMaterial.cpp b/modules/material/src/vkcv/material/PBRMaterial.cpp deleted file mode 100644 index d27e755c06a39e369d22efc997a0b411d067c132..0000000000000000000000000000000000000000 --- a/modules/material/src/vkcv/material/PBRMaterial.cpp +++ /dev/null @@ -1,194 +0,0 @@ -#include "vkcv/material/PBRMaterial.hpp" - - -namespace vkcv::material -{ - PBRMaterial::PBRMaterial( - 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 DescriptorSetHandle& setHandle, - vec4 baseColorFactor, - float metallicFactor, - float roughnessFactor, - float normalScale, - float occlusionStrength, - vec3 emissiveFactor) noexcept : - m_ColorTexture(colorImg), - m_ColorSampler(colorSmp), - m_NormalTexture(normalImg), - m_NormalSampler(normalSmp), - m_MetRoughTexture(metRoughImg), - m_MetRoughSampler(metRoughSmp), - m_OcclusionTexture(occlusionImg), - m_OcclusionSampler(occlusionSmp), - m_EmissiveTexture(emissiveImg), - m_EmissiveSampler(emissiveSmp), - Material(setHandle), - m_BaseColorFactor(baseColorFactor), - m_MetallicFactor(metallicFactor), - m_RoughnessFactor(roughnessFactor), - m_NormalScale(normalScale), - m_OcclusionStrength(occlusionStrength), - m_EmissiveFactor(emissiveFactor) - { - } - - std::vector<DescriptorBinding> PBRMaterial::getDescriptorBindings() noexcept - { - static std::vector<DescriptorBinding> bindings; - - if (bindings.empty()) { - bindings.emplace_back(0, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT); - bindings.emplace_back(1, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT); - bindings.emplace_back(2, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT); - bindings.emplace_back(3, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT); - bindings.emplace_back(4, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT); - bindings.emplace_back(5, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT); - bindings.emplace_back(6, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT); - bindings.emplace_back(7, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT); - bindings.emplace_back(8, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT); - bindings.emplace_back(9, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT); - } - - return bindings; - } - - PBRMaterial PBRMaterial::create( - vkcv::Core* core, - ImageHandle& colorImg, - SamplerHandle& colorSmp, - ImageHandle& normalImg, - SamplerHandle& normalSmp, - ImageHandle& metRoughImg, - SamplerHandle& metRoughSmp, - ImageHandle& occlusionImg, - SamplerHandle& occlusionSmp, - ImageHandle& emissiveImg, - SamplerHandle& emissiveSmp, - vec4 baseColorFactor, - float metallicFactor, - float roughnessFactor, - float normalScale, - float occlusionStrength, - vec3 emissiveFactor) - { - //Test if Images and samplers valid, if not use default - if (!colorImg) { - vkcv::Image defaultColor = core->createImage(vk::Format::eR8G8B8A8Srgb, 1, 1); - vec4 colorData{ 228, 51, 255,1 }; - defaultColor.fill(&colorData); - colorImg = defaultColor.getHandle(); - } - if (!normalImg) { - vkcv::Image defaultNormal = core->createImage(vk::Format::eR8G8B8A8Srgb, 1, 1); - vec4 normalData{ 0, 0, 1,0 }; - defaultNormal.fill(&normalData); - normalImg = defaultNormal.getHandle(); - } - if (!metRoughImg) { - vkcv::Image defaultRough = core->createImage(vk::Format::eR8G8B8A8Srgb, 1, 1); - vec4 roughData{ 228, 51, 255,1 }; - defaultRough.fill(&roughData); - metRoughImg = defaultRough.getHandle(); - } - if (!occlusionImg) { - vkcv::Image defaultOcclusion = core->createImage(vk::Format::eR8G8B8A8Srgb, 1, 1); - vec4 occlusionData{ 228, 51, 255,1 }; - defaultOcclusion.fill(&occlusionData); - occlusionImg = defaultOcclusion.getHandle(); - } - if (!emissiveImg) { - vkcv::Image defaultEmissive = core->createImage(vk::Format::eR8G8B8A8Srgb, 1, 1); - vec4 emissiveData{ 0, 0, 0,1 }; - defaultEmissive.fill(&emissiveData); - emissiveImg = defaultEmissive.getHandle(); - } - if (!colorSmp) { - colorSmp = core->createSampler( - vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerMipmapMode::LINEAR, - vkcv::SamplerAddressMode::REPEAT - ); - } - if (!normalSmp) { - normalSmp = core->createSampler( - vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerMipmapMode::LINEAR, - vkcv::SamplerAddressMode::REPEAT - ); - } - if (!metRoughSmp) { - metRoughSmp = core->createSampler( - vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerMipmapMode::LINEAR, - vkcv::SamplerAddressMode::REPEAT - ); - } - if (!occlusionSmp) { - occlusionSmp = core->createSampler( - vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerMipmapMode::LINEAR, - vkcv::SamplerAddressMode::REPEAT - ); - } - if (!emissiveSmp) { - emissiveSmp = core->createSampler( - vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerMipmapMode::LINEAR, - vkcv::SamplerAddressMode::REPEAT - ); - } - - - - //create descriptorset - vkcv::DescriptorSetHandle descriptorSetHandle = core->createDescriptorSet(getDescriptorBindings()); - //writes - vkcv::DescriptorWrites setWrites; - setWrites.sampledImageWrites = { - vkcv::SampledImageDescriptorWrite(0, colorImg), - vkcv::SampledImageDescriptorWrite(2, normalImg), - vkcv::SampledImageDescriptorWrite(4, metRoughImg), - vkcv::SampledImageDescriptorWrite(6, occlusionImg), - vkcv::SampledImageDescriptorWrite(8, emissiveImg) }; - setWrites.samplerWrites = { - vkcv::SamplerDescriptorWrite(1, colorSmp), - vkcv::SamplerDescriptorWrite(3, normalSmp), - vkcv::SamplerDescriptorWrite(5, metRoughSmp), - vkcv::SamplerDescriptorWrite(7, occlusionSmp), - vkcv::SamplerDescriptorWrite(9, emissiveSmp) }; - core->writeDescriptorSet(descriptorSetHandle, setWrites); - - return PBRMaterial( - colorImg, - colorSmp, - normalImg, - normalSmp, - metRoughImg, - metRoughSmp, - occlusionImg, - occlusionSmp, - emissiveImg, - emissiveSmp, - descriptorSetHandle, - baseColorFactor, - metallicFactor, - roughnessFactor, - normalScale, - occlusionStrength, - emissiveFactor); - } -} \ No newline at end of file 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..48fd6c82b3fefb38e59dd799d8b12fddddf8e63c 160000 --- a/modules/shader_compiler/lib/glslang +++ b/modules/shader_compiler/lib/glslang @@ -1 +1 @@ -Subproject commit fe15158676657bf965e41c32e15ae5db7ea2ab6a +Subproject commit 48fd6c82b3fefb38e59dd799d8b12fddddf8e63c 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..1467bf22616764e00399ada69d5f866b929416d5 --- /dev/null +++ b/modules/shader_compiler/src/vkcv/shader/Compiler.cpp @@ -0,0 +1,35 @@ + +#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) { + for (const auto& stage : stages) { + compile( + stage.first, + stage.second, + [&program](ShaderStage shaderStage, const std::filesystem::path& path) { + 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..47a963d43f0444216a7a972987a7d584333a1fc8 --- /dev/null +++ b/modules/upscaling/CMakeLists.txt @@ -0,0 +1,53 @@ +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 +) + +# 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 NVIDIAImageScaling +include(config/NVIDIAImageScaling.cmake) + +# 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..57f6dbdc8a9457982580170755a751fee74cefa6 --- /dev/null +++ b/modules/upscaling/README.md @@ -0,0 +1,16 @@ +# 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/) | ✅ | + +## 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/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..4f0bfea8499aaa04ce9d7503b47b08e4cd8cde26 --- /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 comands 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/FSRUpscaling.hpp b/modules/upscaling/include/vkcv/upscaling/FSRUpscaling.hpp new file mode 100644 index 0000000000000000000000000000000000000000..64bc78aca4fe42ff813d05fc016a70c7108bd557 --- /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 comands 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..913983400808f3353adf3a1c653045345d36c6f1 --- /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 comands 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..4d04746f0849615a112d04827c7af41811031116 --- /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 comands 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/NVIDIAImageScaling b/modules/upscaling/lib/NVIDIAImageScaling new file mode 160000 index 0000000000000000000000000000000000000000..7a468267104585ce5cd683aebd8e4cb74f826807 --- /dev/null +++ b/modules/upscaling/lib/NVIDIAImageScaling @@ -0,0 +1 @@ +Subproject commit 7a468267104585ce5cd683aebd8e4cb74f826807 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/FSRUpscaling.cpp b/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp new file mode 100644 index 0000000000000000000000000000000000000000..247161e35718cde352da5461b51f30c4fe8570c7 --- /dev/null +++ b/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp @@ -0,0 +1,398 @@ + +#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))) { + m_intermediateImage = m_core.createImage( + m_core.getImageFormat(output), + outputWidth, outputHeight,1, + false, + true + ); + + 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, + {DescriptorSetUsage(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, + {DescriptorSetUsage(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, + {DescriptorSetUsage(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..2b25c9132b20368f37568d95608b23b89645016e --- /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, + {DescriptorSetUsage(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 1c6e3afe2347f6ef8ea8a62be7acbe0ea750497d..1fffd2dab6cd34368d7140c857e7b885cea998cb 100644 --- a/projects/CMakeLists.txt +++ b/projects/CMakeLists.txt @@ -1,8 +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(particle_simulation) add_subdirectory(first_scene) +add_subdirectory(head_demo) +add_subdirectory(indirect_dispatch) +add_subdirectory(indirect_draw) +add_subdirectory(mesh_shader) +add_subdirectory(particle_simulation) +add_subdirectory(path_tracer) +add_subdirectory(rtx_ambient_occlusion) +add_subdirectory(saf_r) +add_subdirectory(sph) add_subdirectory(voxelization) +add_subdirectory(wobble_bobble) \ 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/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..42af6910cd0102f01df29d470269882ebd1cb0d0 --- /dev/null +++ b/projects/bindless_textures/src/main.cpp @@ -0,0 +1,256 @@ +#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); + cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); + + cameraManager.getCamera(camHandle0).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, + 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/downsample.comp b/projects/bloom/resources/shaders/downsample.comp deleted file mode 100644 index 2ab00c7c92798769153634f3479c5b7f3fb61d94..0000000000000000000000000000000000000000 --- a/projects/bloom/resources/shaders/downsample.comp +++ /dev/null @@ -1,76 +0,0 @@ -#version 450 -#extension GL_ARB_separate_shader_objects : enable - -layout(set=0, binding=0) uniform texture2D inBlurImage; -layout(set=0, binding=1) uniform sampler inImageSampler; -layout(set=0, binding=2, r11f_g11f_b10f) uniform writeonly image2D outBlurImage; - -layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; - - -void main() -{ - if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outBlurImage)))){ - return; - } - - ivec2 pixel_coord = ivec2(gl_GlobalInvocationID.xy); - vec2 pixel_size = vec2(1.0f) / imageSize(outBlurImage); - vec2 UV = pixel_coord.xy * pixel_size; - vec2 UV_offset = UV + 0.5f * pixel_size; - - vec2 color_fetches[13] = { - // center neighbourhood (RED) - vec2(-1, 1), // LT - vec2(-1, -1), // LB - vec2( 1, -1), // RB - vec2( 1, 1), // RT - - vec2(-2, 2), // LT - vec2( 0, 2), // CT - vec2( 2, 2), // RT - - vec2(0 ,-2), // LC - vec2(0 , 0), // CC - vec2(2, 0), // CR - - vec2(-2, -2), // LB - vec2(0 , -2), // CB - vec2(2 , -2) // RB - }; - - float color_weights[13] = { - // 0.5f - 1.f/8.f, - 1.f/8.f, - 1.f/8.f, - 1.f/8.f, - - // 0.125f - 1.f/32.f, - 1.f/16.f, - 1.f/32.f, - - // 0.25f - 1.f/16.f, - 1.f/8.f, - 1.f/16.f, - - // 0.125f - 1.f/32.f, - 1.f/16.f, - 1.f/32.f - }; - - vec3 sampled_color = vec3(0.0f); - - for(uint i = 0; i < 13; i++) - { - vec2 color_fetch = UV_offset + color_fetches[i] * pixel_size; - vec3 color = texture(sampler2D(inBlurImage, inImageSampler), color_fetch).rgb; - color *= color_weights[i]; - sampled_color += color; - } - - imageStore(outBlurImage, pixel_coord, vec4(sampled_color, 1.f)); -} \ 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/lensFlares.comp b/projects/bloom/resources/shaders/lensFlares.comp deleted file mode 100644 index ce27d8850b709f61332d467914ddc944dc63109f..0000000000000000000000000000000000000000 --- a/projects/bloom/resources/shaders/lensFlares.comp +++ /dev/null @@ -1,109 +0,0 @@ -#version 450 -#extension GL_ARB_separate_shader_objects : enable - -layout(set=0, binding=0) uniform texture2D blurBuffer; -layout(set=0, binding=1) uniform sampler linearSampler; -layout(set=0, binding=2, r11f_g11f_b10f) uniform image2D lensBuffer; - -layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; - -vec3 sampleColorChromaticAberration(vec2 _uv) -{ - vec2 toCenter = (vec2(0.5) - _uv); - - vec3 colorScales = vec3(-1, 0, 1); - float aberrationScale = 0.1; - vec3 scaleFactors = colorScales * aberrationScale; - - float r = texture(sampler2D(blurBuffer, linearSampler), _uv + toCenter * scaleFactors.r).r; - float g = texture(sampler2D(blurBuffer, linearSampler), _uv + toCenter * scaleFactors.g).g; - float b = texture(sampler2D(blurBuffer, linearSampler), _uv + toCenter * scaleFactors.b).b; - return vec3(r, g, b); -} - -// _uv assumed to be flipped UV coordinates! -vec3 ghost_vectors(vec2 _uv) -{ - vec2 ghost_vec = (vec2(0.5f) - _uv); - - const uint c_ghost_count = 64; - const float c_ghost_spacing = length(ghost_vec) / c_ghost_count; - - ghost_vec *= c_ghost_spacing; - - vec3 ret_color = vec3(0.0f); - - for (uint i = 0; i < c_ghost_count; ++i) - { - // sample scene color - vec2 s_uv = fract(_uv + ghost_vec * vec2(i)); - vec3 s = sampleColorChromaticAberration(s_uv); - - // tint/weight - float d = distance(s_uv, vec2(0.5)); - float weight = 1.0f - smoothstep(0.0f, 0.75f, d); - s *= weight; - - ret_color += s; - } - - ret_color /= c_ghost_count; - return ret_color; -} - -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; - - 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; - float d = distance(w_uv, vec2(0.5)); // distance to center - - float distance_to_halo = abs(d - c_radius); - - float halo_weight = 0.0f; - if(abs(d - c_radius) <= c_halo_thickness) - { - 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; - } - - return sampleColorChromaticAberration(_uv + halo_vec) * halo_weight; -} - - - -void main() -{ - if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(lensBuffer)))){ - return; - } - - ivec2 pixel_coord = ivec2(gl_GlobalInvocationID.xy); - vec2 pixel_size = vec2(1.0f) / imageSize(lensBuffer); - vec2 UV = pixel_coord.xy * pixel_size; - - vec2 flipped_UV = vec2(1.0f) - UV; - - vec3 color = vec3(0.0f); - - color += ghost_vectors(flipped_UV); - color += halo(UV); - color *= 0.5f; - - imageStore(lensBuffer, pixel_coord, vec4(color, 0.0f)); -} \ 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/resources/shaders/upsample.comp b/projects/bloom/resources/shaders/upsample.comp deleted file mode 100644 index 0ddeedb5b5af9e476dc19012fed6430544006c0e..0000000000000000000000000000000000000000 --- a/projects/bloom/resources/shaders/upsample.comp +++ /dev/null @@ -1,45 +0,0 @@ -#version 450 -#extension GL_ARB_separate_shader_objects : enable - -layout(set=0, binding=0) uniform texture2D inUpsampleImage; -layout(set=0, binding=1) uniform sampler inImageSampler; -layout(set=0, binding=2, r11f_g11f_b10f) uniform image2D outUpsampleImage; - -layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; - -void main() -{ - if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outUpsampleImage)))){ - return; - } - - - ivec2 pixel_coord = ivec2(gl_GlobalInvocationID.xy); - vec2 pixel_size = vec2(1.0f) / imageSize(outUpsampleImage); - vec2 UV = pixel_coord.xy * pixel_size; - - const float gauss_kernel[3] = {1.f, 2.f, 1.f}; - const float gauss_weight = 16.f; - - vec3 sampled_color = vec3(0.f); - - for(int i = -1; i <= 1; i++) - { - for(int j = -1; j <= 1; j++) - { - vec2 sample_location = UV + vec2(j, i) * pixel_size; - vec3 color = texture(sampler2D(inUpsampleImage, inImageSampler), sample_location).rgb; - color *= gauss_kernel[j+1]; - color *= gauss_kernel[i+1]; - color /= gauss_weight; - - sampled_color += color; - } - } - - //vec3 prev_color = imageLoad(outUpsampleImage, pixel_coord).rgb; - //float bloomRimStrength = 0.75f; // adjust this to change strength of bloom - //sampled_color = mix(prev_color, sampled_color, bloomRimStrength); - - imageStore(outUpsampleImage, pixel_coord, vec4(sampled_color, 1.f)); -} \ 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 5d56a94579c43369f01a5718276e8bb405d77eac..0000000000000000000000000000000000000000 --- a/projects/cmd_sync_test/src/main.cpp +++ /dev/null @@ -1,315 +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); - - 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 },1)); - shadowDrawcalls.push_back(vkcv::DrawcallInfo(loadedMesh, {},1)); - } - - 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/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..472b781905fc446c592bef74bc0262792e8c3988 --- /dev/null +++ b/projects/fire_works/src/main.cpp @@ -0,0 +1,1379 @@ + +#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.emplace_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.emplace_back(glm::vec3(0.0f), 1.5f, glm::vec3(0.0f, 1.0f, 1.0f), 10.0f, + + 100, 0, events.size() - 1, 0, + + 10.0f, 1.0f, 0.0f, 0); + + events.emplace_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.emplace_back(glm::vec3(0.0f), 0.75f, glm::vec3(0.0f, 1.5f, 1.0f), 8.0f, + + 150, 0, events.size() - 1, 0, + + 10.0f, 1.0f, 0.0f, 0); + + events.emplace_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.emplace_back(glm::vec3(0.0f), 2.0f, glm::vec3(235.0f, 137.0f, 250.0f), 8.0f, + + 75, 0, events.size() - 1, 0, + + 10.0f, 1.0f, 0.0f, 0); +} + +void InitializeSparklerEvents(std::vector<event_t> &events) { + events.emplace_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.emplace_back(glm::vec3(0.0f), 0.0f, glm::vec3(251.0f, 255.0f, 145.0f), 10.0f, + + 1000, 1, events.size() - 1, 10, + + 0.5f, -1.0f, 0.0f, 100); +} + +void InitializeNestedFireworkEvents(std::vector<event_t>& events) { + events.emplace_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.emplace_back(glm::vec3(0.0f), 0.9f, glm::vec3(0.0f, 1.0f, 1.0f), 7.0f, + + 100, 0, events.size() - 1, 0, + + 10.1f, 1.0f, 0.0f, 0); + + events.emplace_back(glm::vec3(0.0f), 2.0f, glm::vec3(0.0f, 0.0f, 0.0f), 10.0f, + + 100, 0, events.size() - 1, 0, + + 10.0f, 1.0f, 0.0f, 0); + + events.emplace_back(glm::vec3(0.0f), 1.0f, glm::vec3(42.0f,0.0f, 1.0f), 12.5f, + + 100, 0, events.size() - 2, 0, + + 1.0f, 1.0f, 0.5f, 0); + + events.emplace_back(glm::vec3(0.0f), 1.5f, glm::vec3(42.0f, 0.0f, 1.0f), 10.0f, + + 100, 0, events.size() - 1, 0, + + 10.0f, 1.0f, 0.0f, 0); + + events.emplace_back(glm::vec3(0.0f), 2.0f, glm::vec3(42.0f, 0.0f, 1.0f), 10.0f, + + 100, 0, events.size() - 1, 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++) { + colorBuffers[i] = core.createImage( + colorFormat, + swapchainExtent.width, + swapchainExtent.height, + 1, false, true, true + ); + } + + 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::Image voxelRed = vkcv::image( + core, + vk::Format::eR32Uint, + voxelWidth, + voxelHeight, + voxelDepth, + false, true + ); + + vkcv::Image voxelGreen = vkcv::image( + core, + vk::Format::eR32Uint, + voxelWidth, + voxelHeight, + voxelDepth, + false, true + ); + + vkcv::Image voxelBlue = vkcv::image( + core, + vk::Format::eR32Uint, + voxelWidth, + voxelHeight, + voxelDepth, + false, true + ); + + vkcv::Image voxelDensity = vkcv::image( + core, + vk::Format::eR32Uint, + voxelWidth, + voxelHeight, + voxelDepth, + false, true + ); + + std::array<vkcv::ImageHandle, 2> voxelData { + core.createImage( + vk::Format::eR16G16B16A16Sfloat, + voxelWidth, + voxelHeight, + voxelDepth, + false, true + ), + core.createImage( + vk::Format::eR16G16B16A16Sfloat, + voxelWidth, + voxelHeight, + voxelDepth, + false, true + ) + }; + + vkcv::Image voxelSamples = vkcv::image( + core, + colorFormat, + voxelWidth, + voxelHeight, + 1, false, true + ); + + 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 (size_t i = 0; i < colorBuffers.size(); i++) { + if ((core.getImageWidth(colorBuffers[i]) != swapchainWidth) || + (core.getImageHeight(colorBuffers[i]) != swapchainHeight)) { + colorBuffers[i] = core.createImage( + colorFormat, + swapchainWidth, + swapchainHeight, + 1, false, true, true + ); + } + } + + 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/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 e7546fc3a143b3638cceb36869c519336ebec751..1350156140e4946599982a5057b2bab0bab276aa 100644 --- a/projects/first_mesh/src/main.cpp +++ b/projects/first_mesh/src/main.cpp @@ -1,206 +1,148 @@ #include <iostream> +#include <vkcv/Buffer.hpp> #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 <chrono> #include <vkcv/asset/asset_loader.hpp> +#include <vkcv/shader/GLSLCompiler.hpp> -int main(int argc, const char** argv) { - const char* applicationName = "First Mesh"; - - 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 - ); - - 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 - ); + vkcv::ShaderProgram firstMeshProgram; + vkcv::shader::GLSLCompiler compiler; - indexBuffer.fill(mesh.vertexGroups[0].indexBuffer.data); + compiler.compileProgram(firstMeshProgram, { + { vkcv::ShaderStage::VERTEX, "assets/shaders/shader.vert" }, + { vkcv::ShaderStage::FRAGMENT, "assets/shaders/shader.frag" } + }, nullptr); - // 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() + std::vector<vkcv::VertexBinding> bindings = vkcv::createVertexBindings( + firstMeshProgram.getVertexAttachments() ); - const vkcv::AttachmentDescription depth_attachment( - vkcv::AttachmentOperation::STORE, - vkcv::AttachmentOperation::CLEAR, - vk::Format::eD32Sfloat - ); + const vkcv::VertexLayout firstMeshLayout { bindings }; - vkcv::PassConfig firstMeshPassDefinition({ present_color_attachment, depth_attachment }); - vkcv::PassHandle firstMeshPass = core.createPass(firstMeshPassDefinition); + // 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); + auto set0BindingsExplicitCopy = set0Bindings; - if (!firstMeshPass) { - std::cout << "Error. Could not create renderpass. Exiting." << std::endl; - return EXIT_FAILURE; - } + vkcv::DescriptorSetLayoutHandle setLayoutHandle = core.createDescriptorSetLayout(set0Bindings); + vkcv::DescriptorSetLayoutHandle setLayoutHandleCopy = core.createDescriptorSetLayout(set0BindingsExplicitCopy); - 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 - ); + + { + auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); + texture.recordMipChainGeneration(cmdStream, core.getDownsampler()); + core.submitCommandStream(cmdStream, false); + } - 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()) }; + 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 },1); + + 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::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, -3)); - - 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; + cameraManager.getCamera(camHandle).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, + 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); 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(); - } + }); 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/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 521818732f7a60eabe9f0c2c080c6d343a71b1d8..9fb7247ec6da805acd2312c3889c45e4a95a3d48 100644 --- a/projects/first_scene/src/main.cpp +++ b/projects/first_scene/src/main.cpp @@ -1,264 +1,146 @@ #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).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},1)); - } - - 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, + 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/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 5bdd55a263f4d81d8f424c056d7d6c0b54ccb1ca..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, {},1); + 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/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..1b6533cfdd3726ad8b1901ae5bbb800d5c65b81d --- /dev/null +++ b/projects/head_demo/src/main.cpp @@ -0,0 +1,243 @@ +#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 <chrono> +#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 = "First Scene"; + + 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); + + 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).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, + swapchainExtent.width, + swapchainExtent.height + ); + + vkcv::ImageHandle colorBuffer = core.createImage( + colorFormat, + swapchainExtent.width, + swapchainExtent.height, + 1, false, true, true + ); + + 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, swapchainWidth, swapchainHeight); + + colorBuffer = core.createImage( + colorFormat, + swapchainExtent.width, + swapchainExtent.height, + 1, false, true, true + ); + + 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..53b0d1b15dc2eeb32308e16ad81d476ed502d370 --- /dev/null +++ b/projects/indirect_dispatch/CMakeLists.txt @@ -0,0 +1,31 @@ +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}) + +# 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) \ No newline at end of file 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/bloom/resources/shaders/shadow.frag b/projects/indirect_dispatch/assets/shaders/sky.frag similarity index 53% rename from projects/bloom/resources/shaders/shadow.frag rename to projects/indirect_dispatch/assets/shaders/sky.frag index 848f853f556660b4900b5db7fb6fc98d57c1cd5b..efc0e03b2d6ee1c71930c866293da66857bd56c7 100644 --- a/projects/bloom/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/bloom/resources/shaders/shadow.vert b/projects/indirect_dispatch/assets/shaders/sky.vert similarity index 58% rename from projects/bloom/resources/shaders/shadow.vert rename to projects/indirect_dispatch/assets/shaders/sky.vert index e0f41d42d575fa64fedbfa04adf89ac0f4aeebe8..44b48cd7f3bfc44e2e43edef0d474581d50608de 100644 --- a/projects/bloom/resources/shaders/shadow.vert +++ b/projects/indirect_dispatch/assets/shaders/sky.vert @@ -4,9 +4,10 @@ layout(location = 0) in vec3 inPosition; layout( push_constant ) uniform constants{ - mat4 mvp; + mat4 viewProjection; }; void main() { - gl_Position = mvp * vec4(inPosition, 1.0); + 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..f17a3df647359018411e5bebcc3175295359678b --- /dev/null +++ b/projects/indirect_dispatch/src/App.cpp @@ -0,0 +1,373 @@ +#include "App.hpp" +#include "AppConfig.hpp" + +#include <vkcv/Sampler.hpp> +#include <vkcv/gui/GUI.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" +}; + +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 }, + { VK_KHR_SWAPCHAIN_EXTENSION_NAME })), + 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); + + 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; + } + } + }); + + 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); + if (hasResolutionChanged) { + m_windowWidth = swapchainWidth; + m_windowHeight = swapchainHeight; + + m_renderTargets = createRenderTargets(m_core, m_windowWidth, m_windowHeight); + m_motionBlur.setResolution(m_windowWidth, m_windowHeight); + } + + 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); + + const auto time = frameEndTime - appStartTime; + const float fCurrentTime = std::chrono::duration_cast<std::chrono::milliseconds>(time).count() * 0.001f; + + // update matrices + if (!freezeFrame) { + + viewProjection = m_cameraManager.getActiveCamera().getMVP(); + + for (Object& obj : sceneObjects) { + if (obj.modelMatrixUpdate) { + obj.modelMatrixUpdate(fCurrentTime, obj); + } + obj.mvp = 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(viewProjection); + + m_core.recordDrawcallsToCmdStream( + cmdStream, + m_skyPass.pipeline, + skyPushConstants, + { skyDrawcall }, + renderTargets, + m_windowHandle); + + // motion blur + vkcv::ImageHandle motionBlurOutput; + + if (motionVectorVisualisationMode == eMotionVectorVisualisationMode::None) { + float cameraNear; + float cameraFar; + m_cameraManager.getActiveCamera().getNearFar(cameraNear, cameraFar); + + motionBlurOutput = m_motionBlur.render( + cmdStream, + m_renderTargets.motionBuffer, + m_renderTargets.colorBuffer, + m_renderTargets.depthBuffer, + motionBlurMode, + cameraNear, + cameraFar, + 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); + + 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..26cfbbc38e2a190326b8a17adde5b8e4f44ec1cc --- /dev/null +++ b/projects/indirect_dispatch/src/AppSetup.cpp @@ -0,0 +1,334 @@ +#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, const uint32_t width, const uint32_t height) { + AppRenderTargets targets; + + targets.depthBuffer = core.createImage( + AppConfig::depthBufferFormat, + width, + height, + 1, + false + ); + + targets.colorBuffer = core.createImage( + AppConfig::colorBufferFormat, + width, + height, + 1, + false, + false, + true + ); + + targets.motionBuffer = core.createImage( + AppConfig::motionBufferFormat, + width, + height, + 1, + false, + false, + true + ); + + 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..41e020c357a3d868775a581170596e1748e39700 --- /dev/null +++ b/projects/indirect_dispatch/src/AppSetup.hpp @@ -0,0 +1,49 @@ +#pragma once +#include <vkcv/Core.hpp> + +struct AppRenderTargets { + vkcv::ImageHandle depthBuffer; + vkcv::ImageHandle colorBuffer; + vkcv::ImageHandle motionBuffer; +}; + +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, const uint32_t width, const uint32_t height); \ 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..0244c4ae9519ea6c6f818e024930e70c15e7a289 --- /dev/null +++ b/projects/indirect_dispatch/src/MotionBlurSetup.cpp @@ -0,0 +1,62 @@ +#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; + + targets.motionMax = core.createImage( + MotionBlurConfig::motionVectorTileFormat, + motionMaxWidth, + motionMaxheight, + 1, + false, + true + ); + + targets.motionMaxNeighbourhood = core.createImage( + MotionBlurConfig::motionVectorTileFormat, + motionMaxWidth, + motionMaxheight, + 1, + false, + true + ); + + targets.motionMin = core.createImage( + MotionBlurConfig::motionVectorTileFormat, + motionMaxWidth, + motionMaxheight, + 1, + false, + true + ); + + targets.motionMinNeighbourhood = core.createImage( + MotionBlurConfig::motionVectorTileFormat, + motionMaxWidth, + motionMaxheight, + 1, + false, + true + ); + + targets.outputColor = core.createImage( + MotionBlurConfig::outputColorFormat, + width, + height, + 1, + false, + true + ); + + 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/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..5951c8c0653011810902a6e8499d02c96d07fd1c --- /dev/null +++ b/projects/indirect_draw/src/main.cpp @@ -0,0 +1,590 @@ +#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, 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/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..512e1e6d1e6ea694289cb862ecae425b6bf8ee66 --- /dev/null +++ b/projects/mesh_shader/src/main.cpp @@ -0,0 +1,401 @@ +#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(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, + 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/particle_simulation/CMakeLists.txt b/projects/particle_simulation/CMakeLists.txt index 2a665202c521ac10ae94905cb7580205e897eef9..860bfa15a4b77b71ac79e3433326feb6aa3d7123 100644 --- a/projects/particle_simulation/CMakeLists.txt +++ b/projects/particle_simulation/CMakeLists.txt @@ -2,34 +2,30 @@ cmake_minimum_required(VERSION 3.16) project(particle_simulation) # 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(particle_simulation +add_project(particle_simulation src/main.cpp src/ParticleSystem.hpp src/ParticleSystem.cpp src/Particle.hpp - src/Particle.cpp - src/BloomAndFlares.hpp - src/BloomAndFlares.cpp) - -# this should fix the execution path to load local files from the project (for MSVC) -if(MSVC) - set_target_properties(particle_simulation PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) - set_target_properties(particle_simulation 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(particle_simulation PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) -endif() + 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}) +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) +target_link_libraries(particle_simulation + vkcv + vkcv_testing + vkcv_camera + vkcv_shader_compiler + vkcv_effects) diff --git a/projects/particle_simulation/shaders/bloom/composite.comp b/projects/particle_simulation/shaders/bloom/composite.comp deleted file mode 100644 index 87b5ddb975106232d1cd3b6e5b8dc7e623dd0b59..0000000000000000000000000000000000000000 --- a/projects/particle_simulation/shaders/bloom/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.01f; - float lens_weight = 0.f; - 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/particle_simulation/shaders/bloom/downsample.comp b/projects/particle_simulation/shaders/bloom/downsample.comp deleted file mode 100644 index 2ab00c7c92798769153634f3479c5b7f3fb61d94..0000000000000000000000000000000000000000 --- a/projects/particle_simulation/shaders/bloom/downsample.comp +++ /dev/null @@ -1,76 +0,0 @@ -#version 450 -#extension GL_ARB_separate_shader_objects : enable - -layout(set=0, binding=0) uniform texture2D inBlurImage; -layout(set=0, binding=1) uniform sampler inImageSampler; -layout(set=0, binding=2, r11f_g11f_b10f) uniform writeonly image2D outBlurImage; - -layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; - - -void main() -{ - if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outBlurImage)))){ - return; - } - - ivec2 pixel_coord = ivec2(gl_GlobalInvocationID.xy); - vec2 pixel_size = vec2(1.0f) / imageSize(outBlurImage); - vec2 UV = pixel_coord.xy * pixel_size; - vec2 UV_offset = UV + 0.5f * pixel_size; - - vec2 color_fetches[13] = { - // center neighbourhood (RED) - vec2(-1, 1), // LT - vec2(-1, -1), // LB - vec2( 1, -1), // RB - vec2( 1, 1), // RT - - vec2(-2, 2), // LT - vec2( 0, 2), // CT - vec2( 2, 2), // RT - - vec2(0 ,-2), // LC - vec2(0 , 0), // CC - vec2(2, 0), // CR - - vec2(-2, -2), // LB - vec2(0 , -2), // CB - vec2(2 , -2) // RB - }; - - float color_weights[13] = { - // 0.5f - 1.f/8.f, - 1.f/8.f, - 1.f/8.f, - 1.f/8.f, - - // 0.125f - 1.f/32.f, - 1.f/16.f, - 1.f/32.f, - - // 0.25f - 1.f/16.f, - 1.f/8.f, - 1.f/16.f, - - // 0.125f - 1.f/32.f, - 1.f/16.f, - 1.f/32.f - }; - - vec3 sampled_color = vec3(0.0f); - - for(uint i = 0; i < 13; i++) - { - vec2 color_fetch = UV_offset + color_fetches[i] * pixel_size; - vec3 color = texture(sampler2D(inBlurImage, inImageSampler), color_fetch).rgb; - color *= color_weights[i]; - sampled_color += color; - } - - imageStore(outBlurImage, pixel_coord, vec4(sampled_color, 1.f)); -} \ No newline at end of file diff --git a/projects/particle_simulation/shaders/bloom/lensFlares.comp b/projects/particle_simulation/shaders/bloom/lensFlares.comp deleted file mode 100644 index ce27d8850b709f61332d467914ddc944dc63109f..0000000000000000000000000000000000000000 --- a/projects/particle_simulation/shaders/bloom/lensFlares.comp +++ /dev/null @@ -1,109 +0,0 @@ -#version 450 -#extension GL_ARB_separate_shader_objects : enable - -layout(set=0, binding=0) uniform texture2D blurBuffer; -layout(set=0, binding=1) uniform sampler linearSampler; -layout(set=0, binding=2, r11f_g11f_b10f) uniform image2D lensBuffer; - -layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; - -vec3 sampleColorChromaticAberration(vec2 _uv) -{ - vec2 toCenter = (vec2(0.5) - _uv); - - vec3 colorScales = vec3(-1, 0, 1); - float aberrationScale = 0.1; - vec3 scaleFactors = colorScales * aberrationScale; - - float r = texture(sampler2D(blurBuffer, linearSampler), _uv + toCenter * scaleFactors.r).r; - float g = texture(sampler2D(blurBuffer, linearSampler), _uv + toCenter * scaleFactors.g).g; - float b = texture(sampler2D(blurBuffer, linearSampler), _uv + toCenter * scaleFactors.b).b; - return vec3(r, g, b); -} - -// _uv assumed to be flipped UV coordinates! -vec3 ghost_vectors(vec2 _uv) -{ - vec2 ghost_vec = (vec2(0.5f) - _uv); - - const uint c_ghost_count = 64; - const float c_ghost_spacing = length(ghost_vec) / c_ghost_count; - - ghost_vec *= c_ghost_spacing; - - vec3 ret_color = vec3(0.0f); - - for (uint i = 0; i < c_ghost_count; ++i) - { - // sample scene color - vec2 s_uv = fract(_uv + ghost_vec * vec2(i)); - vec3 s = sampleColorChromaticAberration(s_uv); - - // tint/weight - float d = distance(s_uv, vec2(0.5)); - float weight = 1.0f - smoothstep(0.0f, 0.75f, d); - s *= weight; - - ret_color += s; - } - - ret_color /= c_ghost_count; - return ret_color; -} - -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; - - 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; - float d = distance(w_uv, vec2(0.5)); // distance to center - - float distance_to_halo = abs(d - c_radius); - - float halo_weight = 0.0f; - if(abs(d - c_radius) <= c_halo_thickness) - { - 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; - } - - return sampleColorChromaticAberration(_uv + halo_vec) * halo_weight; -} - - - -void main() -{ - if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(lensBuffer)))){ - return; - } - - ivec2 pixel_coord = ivec2(gl_GlobalInvocationID.xy); - vec2 pixel_size = vec2(1.0f) / imageSize(lensBuffer); - vec2 UV = pixel_coord.xy * pixel_size; - - vec2 flipped_UV = vec2(1.0f) - UV; - - vec3 color = vec3(0.0f); - - color += ghost_vectors(flipped_UV); - color += halo(UV); - color *= 0.5f; - - imageStore(lensBuffer, pixel_coord, vec4(color, 0.0f)); -} \ No newline at end of file diff --git a/projects/particle_simulation/shaders/bloom/upsample.comp b/projects/particle_simulation/shaders/bloom/upsample.comp deleted file mode 100644 index 0ddeedb5b5af9e476dc19012fed6430544006c0e..0000000000000000000000000000000000000000 --- a/projects/particle_simulation/shaders/bloom/upsample.comp +++ /dev/null @@ -1,45 +0,0 @@ -#version 450 -#extension GL_ARB_separate_shader_objects : enable - -layout(set=0, binding=0) uniform texture2D inUpsampleImage; -layout(set=0, binding=1) uniform sampler inImageSampler; -layout(set=0, binding=2, r11f_g11f_b10f) uniform image2D outUpsampleImage; - -layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; - -void main() -{ - if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outUpsampleImage)))){ - return; - } - - - ivec2 pixel_coord = ivec2(gl_GlobalInvocationID.xy); - vec2 pixel_size = vec2(1.0f) / imageSize(outUpsampleImage); - vec2 UV = pixel_coord.xy * pixel_size; - - const float gauss_kernel[3] = {1.f, 2.f, 1.f}; - const float gauss_weight = 16.f; - - vec3 sampled_color = vec3(0.f); - - for(int i = -1; i <= 1; i++) - { - for(int j = -1; j <= 1; j++) - { - vec2 sample_location = UV + vec2(j, i) * pixel_size; - vec3 color = texture(sampler2D(inUpsampleImage, inImageSampler), sample_location).rgb; - color *= gauss_kernel[j+1]; - color *= gauss_kernel[i+1]; - color /= gauss_weight; - - sampled_color += color; - } - } - - //vec3 prev_color = imageLoad(outUpsampleImage, pixel_coord).rgb; - //float bloomRimStrength = 0.75f; // adjust this to change strength of bloom - //sampled_color = mix(prev_color, sampled_color, bloomRimStrength); - - imageStore(outUpsampleImage, pixel_coord, vec4(sampled_color, 1.f)); -} \ No newline at end of file diff --git a/projects/particle_simulation/src/BloomAndFlares.cpp b/projects/particle_simulation/src/BloomAndFlares.cpp deleted file mode 100644 index 23ace2bc35a2e421613718c62380f9161a408f70..0000000000000000000000000000000000000000 --- a/projects/particle_simulation/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, - "shaders/bloom/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, - "shaders/bloom/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, - "shaders/bloom/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, - "shaders/bloom/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 < std::min((uint32_t)m_DownsampleDescSets.size(), m_Blur.getMipCount()); 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>(3) - ); - - // 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/particle_simulation/src/BloomAndFlares.hpp b/projects/particle_simulation/src/BloomAndFlares.hpp deleted file mode 100644 index 756b1ca154ea5232df04eb09a88bb743c5bd28aa..0000000000000000000000000000000000000000 --- a/projects/particle_simulation/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/particle_simulation/src/Particle.cpp b/projects/particle_simulation/src/Particle.cpp index 387728eb366430e4373282da785bbff47de17e7a..b80d063d382c9ae1cb63887388cce065b8289b63 100644 --- a/projects/particle_simulation/src/Particle.cpp +++ b/projects/particle_simulation/src/Particle.cpp @@ -3,16 +3,17 @@ Particle::Particle(glm::vec3 position, glm::vec3 velocity, float lifeTime) : m_position(position), -m_velocity(velocity), -m_lifeTime(lifeTime), -m_reset_velocity(velocity) + m_lifeTime(lifeTime), + m_velocity(velocity), + m_mass(1.0f), + m_reset_velocity(velocity) {} const glm::vec3& Particle::getPosition()const{ return m_position; } -const bool Particle::isAlive()const{ +bool Particle::isAlive()const{ return m_lifeTime > 0.f; } diff --git a/projects/particle_simulation/src/Particle.hpp b/projects/particle_simulation/src/Particle.hpp index f374218fd8a08f1e1bf367bdc899a71c55ea1b78..73e7cbf517709ee03274cfd199081ade3f756545 100644 --- a/projects/particle_simulation/src/Particle.hpp +++ b/projects/particle_simulation/src/Particle.hpp @@ -17,7 +17,7 @@ public: void update( const float delta ); - const bool isAlive()const; + bool isAlive()const; void setLifeTime( const float lifeTime ); @@ -28,7 +28,7 @@ private: glm::vec3 m_position; float m_lifeTime; glm::vec3 m_velocity; - float mass = 1.f; + float m_mass; glm::vec3 m_reset_velocity; - float padding_3 = 0.f; + float padding_3; }; diff --git a/projects/particle_simulation/src/main.cpp b/projects/particle_simulation/src/main.cpp index a22044f0d2588a43a5e7a0f6cba25d9c7460be9f..00bb497b9798064c4b5ac449d6bac8495a1783e8 100644 --- a/projects/particle_simulation/src/main.cpp +++ b/projects/particle_simulation/src/main.cpp @@ -1,118 +1,121 @@ #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 <time.h> +#include <ctime> #include <vkcv/shader/GLSLCompiler.hpp> -#include "BloomAndFlares.hpp" +#include <vkcv/effects/BloomAndFlaresEffect.hpp> int main(int argc, const char **argv) { - const char *applicationName = "Particlesystem"; + const std::string applicationName = "Particlesystem"; uint32_t windowWidth = 800; uint32_t windowHeight = 600; - vkcv::Window window = vkcv::Window::create( - applicationName, - windowWidth, - windowHeight, - true - ); - - vkcv::camera::CameraManager cameraManager(window); - + 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_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 = core.createBuffer<uint16_t>(vkcv::BufferType::INDEX, 3, - vkcv::BufferMemoryType::DEVICE_LOCAL); + 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; - // an example attachment for passes that output to the window - const vkcv::AttachmentDescription present_color_attachment( - vkcv::AttachmentOperation::STORE, - vkcv::AttachmentOperation::CLEAR, - colorFormat); - - - vkcv::PassConfig particlePassDefinition({present_color_attachment}); - vkcv::PassHandle particlePass = core.createPass(particlePassDefinition); + vkcv::PassHandle particlePass = vkcv::passFormat(core, colorFormat); - vkcv::PassConfig computePassDefinition({}); - vkcv::PassHandle computePass = core.createPass(computePassDefinition); - - if (!particlePass || !computePass) + if (!particlePass) { std::cout << "Error. Could not create renderpass. Exiting." << std::endl; return EXIT_FAILURE; } - // use space or use water - bool useSpace = true; + // 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_water1.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, useSpace ? "shaders/shader_space.comp" : "shaders/shader_water.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + compiler.compile(vkcv::ShaderStage::COMPUTE, shaderPathCompute, [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { computeShaderProgram.addShader(shaderStage, path); }); - vkcv::DescriptorSetHandle computeDescriptorSet = core.createDescriptorSet(computeShaderProgram.getReflectedDescriptors()[0]); + 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::VertexBinding(i, { computeVertexAttachments[i] })); + computeBindings.push_back(vkcv::createVertexBinding(i, { computeVertexAttachments[i] })); } - const vkcv::VertexLayout computeLayout(computeBindings); + 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, useSpace ? "shaders/shader_space.frag" : "shaders/shader_water.frag", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + compiler.compile(vkcv::ShaderStage::FRAGMENT, shaderPathFragment, [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { particleShaderProgram.addShader(shaderStage, path); }); - vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet( - particleShaderProgram.getReflectedDescriptors()[0]); + vkcv::DescriptorSetLayoutHandle descriptorSetLayout = core.createDescriptorSetLayout( + particleShaderProgram.getReflectedDescriptors().at(0)); + vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorSetLayout); - vkcv::Buffer<glm::vec3> vertexBuffer = core.createBuffer<glm::vec3>( + 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(0, vertexBuffer.getVulkanHandle())}; + vkcv::vertexBufferBinding(vertexBuffer.getHandle()) + }; 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 particleLayout(bindings); + const vkcv::VertexLayout particleLayout { bindings }; - vkcv::PipelineConfig particlePipelineDefinition{ + vkcv::GraphicsPipelineConfig particlePipelineDefinition ( particleShaderProgram, - UINT32_MAX, - UINT32_MAX, particlePass, {particleLayout}, - {core.getDescriptorSet(descriptorSet).layout}, - true}; - particlePipelineDefinition.m_blendMode = vkcv::BlendMode::Additive; + {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), @@ -120,16 +123,20 @@ int main(int argc, const char **argv) { vertexBuffer.fill(vertices); - vkcv::PipelineHandle particlePipeline = core.createGraphicsPipeline(particlePipelineDefinition); + vkcv::GraphicsPipelineHandle particlePipeline = core.createGraphicsPipeline(particlePipelineDefinition); - vkcv::PipelineHandle computePipeline = core.createComputePipeline(computeShaderProgram, {core.getDescriptorSet(computeDescriptorSet).layout} ); + vkcv::ComputePipelineHandle computePipeline = core.createComputePipeline({ + computeShaderProgram, {computeDescriptorSetLayout} + }); - vkcv::Buffer<glm::vec4> color = core.createBuffer<glm::vec4>( + vkcv::Buffer<glm::vec4> color = vkcv::buffer<glm::vec4>( + core, vkcv::BufferType::UNIFORM, 1 ); - vkcv::Buffer<glm::vec2> position = core.createBuffer<glm::vec2>( + vkcv::Buffer<glm::vec2> position = vkcv::buffer<glm::vec2>( + core, vkcv::BufferType::UNIFORM, 1 ); @@ -139,7 +146,8 @@ int main(int argc, const char **argv) { glm::vec2 lifeTime = glm::vec2(-1.f,8.f); ParticleSystem particleSystem = ParticleSystem( 100000 , minVelocity, maxVelocity, lifeTime); - vkcv::Buffer<Particle> particleBuffer = core.createBuffer<Particle>( + vkcv::Buffer<Particle> particleBuffer = vkcv::buffer<Particle>( + core, vkcv::BufferType::STORAGE, particleSystem.getParticles().size() ); @@ -147,13 +155,12 @@ int main(int argc, const char **argv) { particleBuffer.fill(particleSystem.getParticles()); vkcv::DescriptorWrites setWrites; - setWrites.uniformBufferWrites = {vkcv::UniformBufferDescriptorWrite(0,color.getHandle()), - vkcv::UniformBufferDescriptorWrite(1,position.getHandle())}; - setWrites.storageBufferWrites = { vkcv::StorageBufferDescriptorWrite(2,particleBuffer.getHandle())}; + setWrites.writeUniformBuffer(0, color.getHandle()).writeUniformBuffer(1, position.getHandle()); + setWrites.writeStorageBuffer(2, particleBuffer.getHandle()); core.writeDescriptorSet(descriptorSet, setWrites); vkcv::DescriptorWrites computeWrites; - computeWrites.storageBufferWrites = { vkcv::StorageBufferDescriptorWrite(0,particleBuffer.getHandle())}; + computeWrites.writeStorageBuffer(0, particleBuffer.getHandle()); core.writeDescriptorSet(computeDescriptorSet, computeWrites); if (!particlePipeline || !computePipeline) @@ -164,157 +171,153 @@ int main(int argc, const char **argv) { const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); - const vkcv::Mesh renderMesh({vertexBufferBindings}, particleIndexBuffer.getVulkanHandle(), - particleIndexBuffer.getCount()); - vkcv::DescriptorSetUsage descriptorUsage(0, core.getDescriptorSet(descriptorSet).vulkanHandle); - //vkcv::DrawcallInfo drawcalls(renderMesh, {vkcv::DescriptorSetUsage(0, core.getDescriptorSet(descriptorSet).vulkanHandle)}); + vkcv::VertexData vertexData (vertexBufferBindings); + vertexData.setIndexBuffer(particleIndexBuffer.getHandle()); + vertexData.setCount(particleIndexBuffer.getCount()); + + auto descriptorUsage = vkcv::useDescriptorSet(0, descriptorSet); - glm::vec2 pos = glm::vec2(0.f); - glm::vec3 spawnPosition = glm::vec3(0.f); - glm::vec4 tempPosition = glm::vec4(0.f); + auto pos = glm::vec2(0.f); + auto spawnPosition = glm::vec3(0.f); - window.e_mouseMove.add([&](double offsetX, double offsetY) { + window.e_mouseMove.add([&](double offsetX, double offsetY) { pos = glm::vec2(static_cast<float>(offsetX), static_cast<float>(offsetY)); -// std::cout << offsetX << " , " << offsetY << std::endl; - // borders are assumed to be 0.5 - //pos = glm::vec2((pos.x -0.5f * static_cast<float>(window.getWidth()))/static_cast<float>(window.getWidth()), (pos.y -0.5f * static_cast<float>(window.getHeight()))/static_cast<float>(window.getHeight())); - //borders are assumed to be 1 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()); - glm::vec4 row1 = glm::row(cameraManager.getCamera(0).getView(), 0); - glm::vec4 row2 = glm::row(cameraManager.getCamera(0).getView(), 1); - glm::vec4 row3 = glm::row(cameraManager.getCamera(0).getView(), 2); - glm::vec4 camera_pos = glm::column(cameraManager.getCamera(0).getView(), 3); -// std::cout << "row1: " << row1.x << ", " << row1.y << ", " << row1.z << std::endl; -// std::cout << "row2: " << row2.x << ", " << row2.y << ", " << row2.z << std::endl; -// std::cout << "row3: " << row3.x << ", " << row3.y << ", " << row3.z << std::endl; -// std::cout << "camerapos: " << camera_pos.x << ", " << camera_pos.y << ", " << camera_pos.z << std::endl; -// std::cout << "camerapos: " << camera_pos.x << ", " << camera_pos.y << ", " << camera_pos.z << std::endl; - //glm::vec4 view_axis = glm::row(cameraManager.getCamera().getView(), 2); - // std::cout << "view_axis: " << view_axis.x << ", " << view_axis.y << ", " << view_axis.z << std::endl; - //std::cout << "Front: " << cameraManager.getCamera().getFront().x << ", " << cameraManager.getCamera().getFront().z << ", " << cameraManager.getCamera().getFront().z << std::endl; - glm::mat4 viewmat = cameraManager.getCamera(0).getView(); spawnPosition = glm::vec3(pos.x, pos.y, 0.f); - tempPosition = glm::vec4(spawnPosition, 1.0f); - spawnPosition = glm::vec3(tempPosition.x, tempPosition.y, tempPosition.z); particleSystem.setRespawnPos(glm::vec3(-spawnPosition.x, spawnPosition.y, spawnPosition.z)); -// std::cout << "respawn pos: " << spawnPosition.x << ", " << spawnPosition.y << ", " << spawnPosition.z << std::endl; }); std::vector<glm::mat4> modelMatrices; - std::vector<vkcv::DrawcallInfo> drawcalls; - drawcalls.push_back(vkcv::DrawcallInfo(renderMesh, {descriptorUsage}, particleSystem.getParticles().size())); - - auto start = std::chrono::system_clock::now(); - + + vkcv::InstanceDrawcall drawcall (vertexData, particleSystem.getParticles().size()); + drawcall.useDescriptorSet(0, descriptorSet); + glm::vec4 colorData = glm::vec4(1.0f, 1.0f, 0.0f, 1.0f); - uint32_t camIndex0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); - uint32_t camIndex1 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); - - cameraManager.getCamera(camIndex0).setNearFar(0.1, 30); - cameraManager.getCamera(camIndex1).setNearFar(0.1, 30); - - cameraManager.setActiveCamera(1); - - cameraManager.getCamera(camIndex0).setPosition(glm::vec3(0, 0, -2)); - cameraManager.getCamera(camIndex1).setPosition(glm::vec3(0.0f, 0.0f, -2.0f)); - cameraManager.getCamera(camIndex1).setCenter(glm::vec3(0.0f, 0.0f, 0.0f)); - - vkcv::ImageHandle colorBuffer = core.createImage(colorFormat, windowWidth, windowHeight, 1, false, true, true).getHandle(); - BloomAndFlares bloomAndFlares(&core, colorFormat, windowWidth, windowHeight); - window.e_resize.add([&](int width, int height) { - windowWidth = width; - windowHeight = height; - colorBuffer = core.createImage(colorFormat, windowWidth, windowHeight, 1, false, true, true).getHandle(); - bloomAndFlares.updateImageDimensions(width, height); - }); + auto camHandle0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); + auto camHandle1 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); + + cameraManager.getCamera(camHandle0).setNearFar(0.1, 30); + cameraManager.getCamera(camHandle0).setNearFar(0.1, 30); + + cameraManager.setActiveCamera(camHandle1); + + cameraManager.getCamera(camHandle1).setPosition(glm::vec3(0, 0, -2)); + cameraManager.getCamera(camHandle1).setPosition(glm::vec3(0.0f, 0.0f, -2.0f)); + cameraManager.getCamera(camHandle1).setCenter(glm::vec3(0.0f, 0.0f, 0.0f)); + + const auto swapchainExtent = core.getSwapchainExtent(window.getSwapchain()); + + vkcv::ImageHandle colorBuffer = core.createImage( + colorFormat, + swapchainExtent.width, + swapchainExtent.height, + 1, false, true, true + ); + + 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::DescriptorSetHandle tonemappingDescriptor = core.createDescriptorSet(tonemappingShader.getReflectedDescriptors()[0]); - vkcv::PipelineHandle tonemappingPipe = core.createComputePipeline( + vkcv::DescriptorSetLayoutHandle tonemappingDescriptorLayout = core.createDescriptorSetLayout(tonemappingShader.getReflectedDescriptors().at(0)); + vkcv::DescriptorSetHandle tonemappingDescriptor = core.createDescriptorSet(tonemappingDescriptorLayout); + vkcv::ComputePipelineHandle tonemappingPipe = core.createComputePipeline({ tonemappingShader, - { core.getDescriptorSet(tonemappingDescriptor).layout }); + { tonemappingDescriptorLayout } + }); std::uniform_real_distribution<float> rdm = std::uniform_real_distribution<float>(0.95f, 1.05f); std::default_random_engine rdmEngine; - while (window.isWindowOpen()) { - window.pollEvents(); - - uint32_t swapchainWidth, swapchainHeight; - if (!core.beginFrame(swapchainWidth, swapchainHeight)) { - continue; - } + + 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)) { + colorBuffer = core.createImage( + colorFormat, + swapchainWidth, + swapchainHeight, + 1, false, true, true + ); + } color.fill(&colorData); position.fill(&pos); - auto end = std::chrono::system_clock::now(); - float deltatime = 0.000001 * static_cast<float>( std::chrono::duration_cast<std::chrono::microseconds>(end - start).count() ); - start = end; -// particleSystem.updateParticles(deltatime); - - cameraManager.update(deltatime); + cameraManager.update(dt); // split view and projection to allow for easy billboarding in shader - glm::mat4 renderingMatrices[2]; - renderingMatrices[0] = cameraManager.getActiveCamera().getView(); - renderingMatrices[1] = cameraManager.getActiveCamera().getProjection(); + 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(deltatime, random); + glm::vec2 pushData = glm::vec2(dt, random); - vkcv::PushConstantData pushConstantDataCompute( &pushData, sizeof(glm::vec2)); - uint32_t computeDispatchCount[3] = {static_cast<uint32_t> (std::ceil(particleSystem.getParticles().size()/256.f)),1,1}; - core.recordComputeDispatchToCmdStream(cmdStream, - computePipeline, - computeDispatchCount, - {vkcv::DescriptorSetUsage(0,core.getDescriptorSet(computeDescriptorSet).vulkanHandle)}, - pushConstantDataCompute); + 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::PushConstantData pushConstantDataDraw((void *) &renderingMatrices[0], 2 * sizeof(glm::mat4)); + vkcv::PushConstants pushConstantsDraw (sizeof(renderingMatrices)); + pushConstantsDraw.appendDrawcall(renderingMatrices); + core.recordDrawcallsToCmdStream( cmdStream, - particlePass, particlePipeline, - pushConstantDataDraw, - {drawcalls}, - { colorBuffer }); - - bloomAndFlares.execWholePipeline(cmdStream, colorBuffer); + pushConstantsDraw, + { drawcall }, + { colorBuffer }, + windowHandle + ); + + bloomAndFlares.recordEffect(cmdStream, colorBuffer, colorBuffer); core.prepareImageForStorage(cmdStream, colorBuffer); core.prepareImageForStorage(cmdStream, swapchainInput); vkcv::DescriptorWrites tonemappingDescriptorWrites; - tonemappingDescriptorWrites.storageImageWrites = { - vkcv::StorageImageDescriptorWrite(0, colorBuffer), - vkcv::StorageImageDescriptorWrite(1, swapchainInput) - }; + tonemappingDescriptorWrites.writeStorageImage( + 0, colorBuffer + ).writeStorageImage( + 1, swapchainInput + ); + core.writeDescriptorSet(tonemappingDescriptor, tonemappingDescriptorWrites); - uint32_t tonemappingDispatchCount[3]; - tonemappingDispatchCount[0] = std::ceil(windowWidth / 8.f); - tonemappingDispatchCount[1] = std::ceil(windowHeight / 8.f); - tonemappingDispatchCount[2] = 1; + const auto tonemappingDispatchCount = vkcv::dispatchInvocations( + vkcv::DispatchSize(swapchainWidth, swapchainHeight), + vkcv::DispatchSize(8, 8) + ); core.recordComputeDispatchToCmdStream( cmdStream, tonemappingPipe, tonemappingDispatchCount, - {vkcv::DescriptorSetUsage(0, core.getDescriptorSet(tonemappingDescriptor).vulkanHandle) }, - vkcv::PushConstantData(nullptr, 0)); + { vkcv::useDescriptorSet(0, tonemappingDescriptor) }, + vkcv::PushConstants(0) + ); core.prepareSwapchainImageForPresent(cmdStream); core.submitCommandStream(cmdStream); - core.endFrame(); - } + }); 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/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..53f52c3d724b8db14641108d9b8a0e33e1844600 --- /dev/null +++ b/projects/path_tracer/src/main.cpp @@ -0,0 +1,450 @@ +#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 + ); + + // images + vkcv::ImageHandle outputImage = core.createImage( + vk::Format::eR32G32B32A32Sfloat, + initialWidth, + initialHeight, + 1, + false, + true + ); + + vkcv::ImageHandle meanImage = core.createImage( + vk::Format::eR32G32B32A32Sfloat, + initialWidth, + initialHeight, + 1, + false, + true + ); + + 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) { + + // resize images + outputImage = core.createImage( + vk::Format::eR32G32B32A32Sfloat, + swapchainWidth, + swapchainHeight, + 1, + false, + true + ); + + meanImage = core.createImage( + vk::Format::eR32G32B32A32Sfloat, + swapchainWidth, + swapchainHeight, + 1, + false, + true + ); + + // 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/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/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..f5d8a46feefd070967d0f68742ad0c3b2749f160 --- /dev/null +++ b/projects/rtx_ambient_occlusion/src/main.cpp @@ -0,0 +1,146 @@ +#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).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, + 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/saf_r/.gitignore b/projects/saf_r/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..ff3dff30031efafa24269a9ac0ef93f64f63ded1 --- /dev/null +++ b/projects/saf_r/.gitignore @@ -0,0 +1 @@ +saf_r \ No newline at end of file diff --git a/projects/saf_r/CMakeLists.txt b/projects/saf_r/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..ca315a2d294c792615ad7bc18ec55f132103c270 --- /dev/null +++ b/projects/saf_r/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.16) +project(saf_r) + +# 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(saf_r src/main.cpp "src/safrScene.hpp") + +# including headers of dependencies and the VkCV framework +target_include_directories(saf_r 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(saf_r vkcv vkcv_testing vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_camera vkcv_shader_compiler) diff --git a/projects/saf_r/shaders/raytracing.comp b/projects/saf_r/shaders/raytracing.comp new file mode 100644 index 0000000000000000000000000000000000000000..a7c6b92a646e5c2f753946f74fe7ab78aea44fe6 --- /dev/null +++ b/projects/saf_r/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/saf_r/src/main.cpp b/projects/saf_r/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..35b4737d9645f2ecd379fd8b41f72cf040173f4f --- /dev/null +++ b/projects/saf_r/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 "safrScene.hpp" + +void createQuadraticLightCluster(std::vector<safrScene::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(safrScene::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 = "SAF_R"; + + //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<safrScene::Material> materials; + safrScene::Material ivory(glm::vec4(0.6, 0.3, 0.1, 0.0), glm::vec3(0.4, 0.4, 0.3), 50., 1.0); + safrScene::Material red_rubber(glm::vec4(0.9, 0.1, 0.0, 0.0), glm::vec3(0.3, 0.1, 0.1), 10., 1.0); + safrScene::Material mirror( glm::vec4(0.0, 10.0, 0.8, 0.0), glm::vec3(1.0, 1.0, 1.0), 1425., 1.0); + safrScene::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<safrScene::Sphere> spheres; + spheres.push_back(safrScene::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(safrScene::Sphere(glm::vec3(-1.0, -1.5, -12), 2, glass)); + spheres.push_back(safrScene::Sphere(glm::vec3( 1.5, -0.5, -18), 3, red_rubber)); + spheres.push_back(safrScene::Sphere(glm::vec3( 7, 5, -18), 4, mirror)); + + //lights for the scene + std::vector<safrScene::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<safrScene::Light> lightsBuffer = vkcv::buffer<safrScene::Light>( + core, + vkcv::BufferType::STORAGE, + lights.size() + ); + lightsBuffer.fill(lights); + + vkcv::Buffer<safrScene::Sphere> sphereBuffer = vkcv::buffer<safrScene::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.0f, 0.0f, 0.0f)); + 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/saf_r/src/safrScene.hpp b/projects/saf_r/src/safrScene.hpp new file mode 100644 index 0000000000000000000000000000000000000000..fc149a7b6ba11fd832cf7ec1040d57c401bab87a --- /dev/null +++ b/projects/saf_r/src/safrScene.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 safrScene { + +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/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/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..043d135257ce63398ec34e5f6d47e4f4ffd6c61d --- /dev/null +++ b/projects/sph/src/main.cpp @@ -0,0 +1,422 @@ +#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.1, 30); + cameraManager.getCamera(camHandle0).setPosition(glm::vec3(0, 0, -2.5)); + + cameraManager.setActiveCamera(camHandle1); + + cameraManager.getCamera(camHandle1).setNearFar(0.1, 30); + 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::ImageHandle colorBuffer = core.createImage( + colorFormat, + swapchainExtent.width, + swapchainExtent.height, + 1, false, true, true + ); + + 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)) { + colorBuffer = core.createImage( + colorFormat, + swapchainWidth, + swapchainHeight, + 1, false, true, true + ); + } + + 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 c962409f2e14994f0c38b923de7b9b1a4d198cab..34178021a517485e54ef8dcafe0f170a834e5186 100644 --- a/projects/voxelization/CMakeLists.txt +++ b/projects/voxelization/CMakeLists.txt @@ -2,35 +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 src/ShadowMapping.hpp - src/ShadowMapping.cpp - src/BloomAndFlares.hpp - src/BloomAndFlares.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/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/resources/RadialLUT.png b/projects/voxelization/assets/RadialLUT.png similarity index 100% rename from projects/voxelization/resources/RadialLUT.png rename to projects/voxelization/assets/RadialLUT.png diff --git a/projects/voxelization/resources/Sponza/Sponza.bin b/projects/voxelization/assets/Sponza/Sponza.bin similarity index 100% rename from projects/voxelization/resources/Sponza/Sponza.bin rename to projects/voxelization/assets/Sponza/Sponza.bin diff --git a/projects/voxelization/resources/Sponza/Sponza.gltf b/projects/voxelization/assets/Sponza/Sponza.gltf similarity index 100% rename from projects/voxelization/resources/Sponza/Sponza.gltf rename to projects/voxelization/assets/Sponza/Sponza.gltf diff --git a/projects/voxelization/resources/Sponza/Textures/Arch_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/Arch_Diff.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Arch_Diff.jpg rename to projects/voxelization/assets/Sponza/Textures/Arch_Diff.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Arch_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Arch_Norm.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Arch_Norm.jpg rename to projects/voxelization/assets/Sponza/Textures/Arch_Norm.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Arch_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Arch_Spec.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Arch_Spec.jpg rename to projects/voxelization/assets/Sponza/Textures/Arch_Spec.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Background_Albedo.png b/projects/voxelization/assets/Sponza/Textures/Background_Albedo.png similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Background_Albedo.png rename to projects/voxelization/assets/Sponza/Textures/Background_Albedo.png diff --git a/projects/voxelization/resources/Sponza/Textures/Background_Normal.png b/projects/voxelization/assets/Sponza/Textures/Background_Normal.png similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Background_Normal.png rename to projects/voxelization/assets/Sponza/Textures/Background_Normal.png diff --git a/projects/voxelization/resources/Sponza/Textures/Background_Roughness.png b/projects/voxelization/assets/Sponza/Textures/Background_Roughness.png similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Background_Roughness.png rename to projects/voxelization/assets/Sponza/Textures/Background_Roughness.png diff --git a/projects/voxelization/resources/Sponza/Textures/Bricks_A_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/Bricks_A_Diff.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Bricks_A_Diff.jpg rename to projects/voxelization/assets/Sponza/Textures/Bricks_A_Diff.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Bricks_A_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Bricks_A_Norm.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Bricks_A_Norm.jpg rename to projects/voxelization/assets/Sponza/Textures/Bricks_A_Norm.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Bricks_A_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Bricks_A_Spec.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Bricks_A_Spec.jpg rename to projects/voxelization/assets/Sponza/Textures/Bricks_A_Spec.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Ceiling_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/Ceiling_Diff.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Ceiling_Diff.jpg rename to projects/voxelization/assets/Sponza/Textures/Ceiling_Diff.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Ceiling_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Ceiling_Norm.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Ceiling_Norm.jpg rename to projects/voxelization/assets/Sponza/Textures/Ceiling_Norm.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Ceiling_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Ceiling_Spec.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Ceiling_Spec.jpg rename to projects/voxelization/assets/Sponza/Textures/Ceiling_Spec.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Chain_Diff.png b/projects/voxelization/assets/Sponza/Textures/Chain_Diff.png similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Chain_Diff.png rename to projects/voxelization/assets/Sponza/Textures/Chain_Diff.png diff --git a/projects/voxelization/resources/Sponza/Textures/Chain_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Chain_Norm.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Chain_Norm.jpg rename to projects/voxelization/assets/Sponza/Textures/Chain_Norm.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Cloth1_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Cloth1_Norm.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Cloth1_Norm.jpg rename to projects/voxelization/assets/Sponza/Textures/Cloth1_Norm.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Cloth1_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Cloth1_Spec.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Cloth1_Spec.jpg rename to projects/voxelization/assets/Sponza/Textures/Cloth1_Spec.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Cloth2_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Cloth2_Norm.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Cloth2_Norm.jpg rename to projects/voxelization/assets/Sponza/Textures/Cloth2_Norm.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Cloth2_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Cloth2_Spec.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Cloth2_Spec.jpg rename to projects/voxelization/assets/Sponza/Textures/Cloth2_Spec.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/ClothBlue1_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/ClothBlue1_Diff.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/ClothBlue1_Diff.jpg rename to projects/voxelization/assets/Sponza/Textures/ClothBlue1_Diff.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/ClothBlue2_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/ClothBlue2_Diff.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/ClothBlue2_Diff.jpg rename to projects/voxelization/assets/Sponza/Textures/ClothBlue2_Diff.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/ClothBlue2_Diff_jpg.jpg b/projects/voxelization/assets/Sponza/Textures/ClothBlue2_Diff_jpg.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/ClothBlue2_Diff_jpg.jpg rename to projects/voxelization/assets/Sponza/Textures/ClothBlue2_Diff_jpg.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/ClothGreen1_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/ClothGreen1_Diff.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/ClothGreen1_Diff.jpg rename to projects/voxelization/assets/Sponza/Textures/ClothGreen1_Diff.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/ClothGreen2_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/ClothGreen2_Diff.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/ClothGreen2_Diff.jpg rename to projects/voxelization/assets/Sponza/Textures/ClothGreen2_Diff.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/ClothRed1_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/ClothRed1_Diff.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/ClothRed1_Diff.jpg rename to projects/voxelization/assets/Sponza/Textures/ClothRed1_Diff.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/ClothRed2_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/ClothRed2_Diff.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/ClothRed2_Diff.jpg rename to projects/voxelization/assets/Sponza/Textures/ClothRed2_Diff.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Column_B_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/Column_B_Diff.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Column_B_Diff.jpg rename to projects/voxelization/assets/Sponza/Textures/Column_B_Diff.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Column_B_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Column_B_Norm.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Column_B_Norm.jpg rename to projects/voxelization/assets/Sponza/Textures/Column_B_Norm.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Column_B_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Column_B_Spec.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Column_B_Spec.jpg rename to projects/voxelization/assets/Sponza/Textures/Column_B_Spec.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Column_C_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/Column_C_Diff.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Column_C_Diff.jpg rename to projects/voxelization/assets/Sponza/Textures/Column_C_Diff.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Column_C_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Column_C_Norm.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Column_C_Norm.jpg rename to projects/voxelization/assets/Sponza/Textures/Column_C_Norm.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Column_C_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Column_C_Spec.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Column_C_Spec.jpg rename to projects/voxelization/assets/Sponza/Textures/Column_C_Spec.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Column_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/Column_Diff.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Column_Diff.jpg rename to projects/voxelization/assets/Sponza/Textures/Column_Diff.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Column_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Column_Norm.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Column_Norm.jpg rename to projects/voxelization/assets/Sponza/Textures/Column_Norm.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Column_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Column_Spec.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Column_Spec.jpg rename to projects/voxelization/assets/Sponza/Textures/Column_Spec.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Detail_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/Detail_Diff.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Detail_Diff.jpg rename to projects/voxelization/assets/Sponza/Textures/Detail_Diff.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Detail_norm.jpg b/projects/voxelization/assets/Sponza/Textures/Detail_norm.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Detail_norm.jpg rename to projects/voxelization/assets/Sponza/Textures/Detail_norm.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Detail_spec.jpg b/projects/voxelization/assets/Sponza/Textures/Detail_spec.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Detail_spec.jpg rename to projects/voxelization/assets/Sponza/Textures/Detail_spec.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Fill_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Fill_Spec.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Fill_Spec.jpg rename to projects/voxelization/assets/Sponza/Textures/Fill_Spec.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Flagpole_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/Flagpole_Diff.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Flagpole_Diff.jpg rename to projects/voxelization/assets/Sponza/Textures/Flagpole_Diff.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Flagpole_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Flagpole_Norm.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Flagpole_Norm.jpg rename to projects/voxelization/assets/Sponza/Textures/Flagpole_Norm.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Flagpole_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Flagpole_Spec.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Flagpole_Spec.jpg rename to projects/voxelization/assets/Sponza/Textures/Flagpole_Spec.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Floor_A_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/Floor_A_Diff.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Floor_A_Diff.jpg rename to projects/voxelization/assets/Sponza/Textures/Floor_A_Diff.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Floor_A_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Floor_A_Norm.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Floor_A_Norm.jpg rename to projects/voxelization/assets/Sponza/Textures/Floor_A_Norm.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Floor_A_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Floor_A_Spec.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Floor_A_Spec.jpg rename to projects/voxelization/assets/Sponza/Textures/Floor_A_Spec.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Flower_Diff.png b/projects/voxelization/assets/Sponza/Textures/Flower_Diff.png similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Flower_Diff.png rename to projects/voxelization/assets/Sponza/Textures/Flower_Diff.png diff --git a/projects/voxelization/resources/Sponza/Textures/Flower_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Flower_Norm.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Flower_Norm.jpg rename to projects/voxelization/assets/Sponza/Textures/Flower_Norm.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Flower_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Flower_Spec.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Flower_Spec.jpg rename to projects/voxelization/assets/Sponza/Textures/Flower_Spec.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Lion_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/Lion_Diff.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Lion_Diff.jpg rename to projects/voxelization/assets/Sponza/Textures/Lion_Diff.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Lion_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Lion_Norm.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Lion_Norm.jpg rename to projects/voxelization/assets/Sponza/Textures/Lion_Norm.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Lion_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Lion_Spec.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Lion_Spec.jpg rename to projects/voxelization/assets/Sponza/Textures/Lion_Spec.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Roof_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/Roof_Diff.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Roof_Diff.jpg rename to projects/voxelization/assets/Sponza/Textures/Roof_Diff.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Roof_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Roof_Norm.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Roof_Norm.jpg rename to projects/voxelization/assets/Sponza/Textures/Roof_Norm.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Roof_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Roof_Spec.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Roof_Spec.jpg rename to projects/voxelization/assets/Sponza/Textures/Roof_Spec.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Shield_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Shield_Norm.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Shield_Norm.jpg rename to projects/voxelization/assets/Sponza/Textures/Shield_Norm.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Shield_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Shield_Spec.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Shield_Spec.jpg rename to projects/voxelization/assets/Sponza/Textures/Shield_Spec.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Shield_diff.jpg b/projects/voxelization/assets/Sponza/Textures/Shield_diff.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Shield_diff.jpg rename to projects/voxelization/assets/Sponza/Textures/Shield_diff.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Thorn_Diff.png b/projects/voxelization/assets/Sponza/Textures/Thorn_Diff.png similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Thorn_Diff.png rename to projects/voxelization/assets/Sponza/Textures/Thorn_Diff.png diff --git a/projects/voxelization/resources/Sponza/Textures/Thorn_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Thorn_Norm.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Thorn_Norm.jpg rename to projects/voxelization/assets/Sponza/Textures/Thorn_Norm.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Thorn_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Thorn_Spec.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Thorn_Spec.jpg rename to projects/voxelization/assets/Sponza/Textures/Thorn_Spec.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/VaseRound_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/VaseRound_Diff.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/VaseRound_Diff.jpg rename to projects/voxelization/assets/Sponza/Textures/VaseRound_Diff.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/VaseRound_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/VaseRound_Norm.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/VaseRound_Norm.jpg rename to projects/voxelization/assets/Sponza/Textures/VaseRound_Norm.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/VaseRound_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/VaseRound_Spec.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/VaseRound_Spec.jpg rename to projects/voxelization/assets/Sponza/Textures/VaseRound_Spec.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Vase_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/Vase_Diff.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Vase_Diff.jpg rename to projects/voxelization/assets/Sponza/Textures/Vase_Diff.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Vase_Hanging_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/Vase_Hanging_Diff.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Vase_Hanging_Diff.jpg rename to projects/voxelization/assets/Sponza/Textures/Vase_Hanging_Diff.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Vase_Hanging_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Vase_Hanging_Norm.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Vase_Hanging_Norm.jpg rename to projects/voxelization/assets/Sponza/Textures/Vase_Hanging_Norm.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Vase_Hanging_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Vase_Hanging_Spec.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Vase_Hanging_Spec.jpg rename to projects/voxelization/assets/Sponza/Textures/Vase_Hanging_Spec.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Vase_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Vase_Norm.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Vase_Norm.jpg rename to projects/voxelization/assets/Sponza/Textures/Vase_Norm.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/Vase_spec.jpg b/projects/voxelization/assets/Sponza/Textures/Vase_spec.jpg similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/Vase_spec.jpg rename to projects/voxelization/assets/Sponza/Textures/Vase_spec.jpg diff --git a/projects/voxelization/resources/Sponza/Textures/white.png b/projects/voxelization/assets/Sponza/Textures/white.png similarity index 100% rename from projects/voxelization/resources/Sponza/Textures/white.png rename to projects/voxelization/assets/Sponza/Textures/white.png diff --git a/projects/voxelization/resources/lensDirt.jpg b/projects/voxelization/assets/lensDirt.jpg similarity index 100% rename from projects/voxelization/resources/lensDirt.jpg rename to projects/voxelization/assets/lensDirt.jpg diff --git a/projects/voxelization/resources/shaders/brdf.inc b/projects/voxelization/assets/shaders/brdf.inc similarity index 100% rename from projects/voxelization/resources/shaders/brdf.inc rename to projects/voxelization/assets/shaders/brdf.inc diff --git a/projects/voxelization/resources/shaders/depthPrepass.frag b/projects/voxelization/assets/shaders/depthPrepass.frag similarity index 100% rename from projects/voxelization/resources/shaders/depthPrepass.frag rename to projects/voxelization/assets/shaders/depthPrepass.frag diff --git a/projects/voxelization/resources/shaders/depthPrepass.vert b/projects/voxelization/assets/shaders/depthPrepass.vert similarity index 100% rename from projects/voxelization/resources/shaders/depthPrepass.vert rename to projects/voxelization/assets/shaders/depthPrepass.vert diff --git a/projects/voxelization/resources/shaders/depthToMoments.comp b/projects/voxelization/assets/shaders/depthToMoments.comp similarity index 94% rename from projects/voxelization/resources/shaders/depthToMoments.comp rename to projects/voxelization/assets/shaders/depthToMoments.comp index 5a78d0cb9b748187d12057708fcd0de7658a61ed..79e47cdef02143ed97d53e533f47e822db8c0f6f 100644 --- a/projects/voxelization/resources/shaders/depthToMoments.comp +++ b/projects/voxelization/assets/shaders/depthToMoments.comp @@ -28,9 +28,11 @@ void main(){ 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/resources/shaders/lightInfo.inc b/projects/voxelization/assets/shaders/lightInfo.inc similarity index 100% rename from projects/voxelization/resources/shaders/lightInfo.inc rename to projects/voxelization/assets/shaders/lightInfo.inc diff --git a/projects/voxelization/resources/shaders/luma.inc b/projects/voxelization/assets/shaders/luma.inc similarity index 100% rename from projects/voxelization/resources/shaders/luma.inc rename to projects/voxelization/assets/shaders/luma.inc diff --git a/projects/voxelization/resources/shaders/msaa4XResolve.comp b/projects/voxelization/assets/shaders/msaa4XResolve.comp similarity index 100% rename from projects/voxelization/resources/shaders/msaa4XResolve.comp rename to projects/voxelization/assets/shaders/msaa4XResolve.comp diff --git a/projects/voxelization/resources/shaders/perMeshResources.inc b/projects/voxelization/assets/shaders/perMeshResources.inc similarity index 100% rename from projects/voxelization/resources/shaders/perMeshResources.inc rename to projects/voxelization/assets/shaders/perMeshResources.inc diff --git a/projects/voxelization/resources/shaders/tonemapping.comp b/projects/voxelization/assets/shaders/postEffects.comp similarity index 75% rename from projects/voxelization/resources/shaders/tonemapping.comp rename to projects/voxelization/assets/shaders/postEffects.comp index 8fa07d39ebb56eab857cdccb755a6558f5ae1ec3..c0f9fe1a764bcdabac5501e2f82692c6f476e9e6 100644 --- a/projects/voxelization/resources/shaders/tonemapping.comp +++ b/projects/voxelization/assets/shaders/postEffects.comp @@ -36,40 +36,40 @@ 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); + 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); + 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)); - + 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; } @@ -77,27 +77,27 @@ 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 + + // 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; @@ -106,7 +106,7 @@ float computeLocalContrast(vec2 uv){ lumaMax = max(lumaMax, luma); } } - + return lumaMax - lumaMin; } @@ -121,9 +121,9 @@ vec3 computeChromaticAberrationScale(vec2 uv){ 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; @@ -140,10 +140,10 @@ void main(){ vec2 uv = vec2(coord) / textureRes; float aspectRatio = float(textureRes.x) / textureRes.y; uv = computeDistortedUV(uv, aspectRatio); - vec3 linearColor = sampleColorChromaticAberration(uv); - vec3 tonemapped = ACESFilm(linearColor); + + 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/resources/shaders/shader.frag b/projects/voxelization/assets/shaders/shader.frag similarity index 100% rename from projects/voxelization/resources/shaders/shader.frag rename to projects/voxelization/assets/shaders/shader.frag diff --git a/projects/voxelization/resources/shaders/shader.vert b/projects/voxelization/assets/shaders/shader.vert similarity index 100% rename from projects/voxelization/resources/shaders/shader.vert rename to projects/voxelization/assets/shaders/shader.vert diff --git a/projects/voxelization/resources/shaders/shadow.frag b/projects/voxelization/assets/shaders/shadow.frag similarity index 100% rename from projects/voxelization/resources/shaders/shadow.frag rename to projects/voxelization/assets/shaders/shadow.frag diff --git a/projects/voxelization/resources/shaders/shadow.vert b/projects/voxelization/assets/shaders/shadow.vert similarity index 100% rename from projects/voxelization/resources/shaders/shadow.vert rename to projects/voxelization/assets/shaders/shadow.vert diff --git a/projects/voxelization/resources/shaders/shadowBlur.inc b/projects/voxelization/assets/shaders/shadowBlur.inc similarity index 97% rename from projects/voxelization/resources/shaders/shadowBlur.inc rename to projects/voxelization/assets/shaders/shadowBlur.inc index 06147415f118dca9badd15813b431a68682ce0b0..ed4994ed1ace34afdafff15920d18a2433a3c0a4 100644 --- a/projects/voxelization/resources/shaders/shadowBlur.inc +++ b/projects/voxelization/assets/shaders/shadowBlur.inc @@ -3,7 +3,7 @@ vec4 blurMomentShadowMap1D(ivec2 coord, ivec2 blurDirection, texture2D srcTexture, sampler depthSampler){ - int blurRadius = 9; + int blurRadius = 7; int minOffset = -(blurRadius-1) / 2; int maxOffset = -minOffset; diff --git a/projects/voxelization/resources/shaders/shadowBlurX.comp b/projects/voxelization/assets/shaders/shadowBlurX.comp similarity index 90% rename from projects/voxelization/resources/shaders/shadowBlurX.comp rename to projects/voxelization/assets/shaders/shadowBlurX.comp index 45b91aad71673347dbf607fecef92463ef1c3c88..41d127fdf5ce46dec883d49af4f284b5787d5d38 100644 --- a/projects/voxelization/resources/shaders/shadowBlurX.comp +++ b/projects/voxelization/assets/shaders/shadowBlurX.comp @@ -18,6 +18,6 @@ void main(){ } 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/resources/shaders/shadowBlurY.comp b/projects/voxelization/assets/shaders/shadowBlurY.comp similarity index 82% rename from projects/voxelization/resources/shaders/shadowBlurY.comp rename to projects/voxelization/assets/shaders/shadowBlurY.comp index 51d4df054b0d99e54149863a5967143518f61dd2..c1710d7d6c75ef0093fecfe708272f56f9541eaf 100644 --- a/projects/voxelization/resources/shaders/shadowBlurY.comp +++ b/projects/voxelization/assets/shaders/shadowBlurY.comp @@ -16,10 +16,8 @@ void main(){ if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outImage)))){ return; } - ivec2 coord = ivec2(gl_GlobalInvocationID.xy); - vec2 pixelSize = vec2(1) / textureSize(sampler2D(srcTexture, depthSampler), 0); - + 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/resources/shaders/shadowMapping.inc b/projects/voxelization/assets/shaders/shadowMapping.inc similarity index 72% rename from projects/voxelization/resources/shaders/shadowMapping.inc rename to projects/voxelization/assets/shaders/shadowMapping.inc index c56ae8985c5c5fcef780b622d8b888f1081af74c..9124a05c310c2cc16e6b02802f5adb36bde42804 100644 --- a/projects/voxelization/resources/shaders/shadowMapping.inc +++ b/projects/voxelization/assets/shaders/shadowMapping.inc @@ -6,7 +6,8 @@ // 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.5),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]); @@ -39,24 +40,23 @@ float ComputeMSMShadowIntensity(vec4 _4Moments, float FragmentDepth, float Depth } vec4 quantizeMoments(vec4 moments){ - mat4 T = mat4( - -2.07224649, 13.7948857237, 0.105877704, 9.7924062118, - 32.23703778, -59.4683975703, -1.9077466311, -33.7652110555, - -68.571074599, 82.0359750338, 9.3496555107, 47.9456096605, - 39.3703274134, -35.364903257, -6.6543490743, -23.9728048165); - vec4 quantized = T * moments; - quantized[0] += 0.0359558848; - return quantized; + 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[0] -= 0.0359558848; - mat4 T = mat4( - 0.2227744146, 0.1549679261, 0.1451988946, 0.163127443, - 0.0771972861, 0.1394629426, 0.2120202157, 0.2591432266, - 0.7926986636, 0.7963415838, 0.7258694464, 0.6539092497, - 0.0319417555, -0.1722823173, -0.2758014811, -0.3376131734); - return T * 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) @@ -78,18 +78,20 @@ float shadowTest(vec3 worldPos, LightInfo lightInfo, texture2D shadowMap, sample 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.0003; + float momentBias = 0.0006; float shadow = ComputeMSMShadowIntensity(shadowMapSample, lightPos.z, depthBias, momentBias); - return reduceLightBleeding(shadow, 0.1f); + 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/resources/shaders/sky.frag b/projects/voxelization/assets/shaders/sky.frag similarity index 100% rename from projects/voxelization/resources/shaders/sky.frag rename to projects/voxelization/assets/shaders/sky.frag diff --git a/projects/voxelization/resources/shaders/sky.vert b/projects/voxelization/assets/shaders/sky.vert similarity index 100% rename from projects/voxelization/resources/shaders/sky.vert rename to projects/voxelization/assets/shaders/sky.vert 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/resources/shaders/voxel.inc b/projects/voxelization/assets/shaders/voxel.inc similarity index 100% rename from projects/voxelization/resources/shaders/voxel.inc rename to projects/voxelization/assets/shaders/voxel.inc diff --git a/projects/voxelization/resources/shaders/voxelBufferToImage.comp b/projects/voxelization/assets/shaders/voxelBufferToImage.comp similarity index 100% rename from projects/voxelization/resources/shaders/voxelBufferToImage.comp rename to projects/voxelization/assets/shaders/voxelBufferToImage.comp diff --git a/projects/voxelization/resources/shaders/voxelReset.comp b/projects/voxelization/assets/shaders/voxelReset.comp similarity index 100% rename from projects/voxelization/resources/shaders/voxelReset.comp rename to projects/voxelization/assets/shaders/voxelReset.comp diff --git a/projects/voxelization/resources/shaders/voxelSecondaryBounce.comp b/projects/voxelization/assets/shaders/voxelSecondaryBounce.comp similarity index 100% rename from projects/voxelization/resources/shaders/voxelSecondaryBounce.comp rename to projects/voxelization/assets/shaders/voxelSecondaryBounce.comp 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 100% rename from projects/voxelization/resources/shaders/voxelVisualisation.vert rename to projects/voxelization/assets/shaders/voxelVisualisation.vert diff --git a/projects/voxelization/resources/shaders/voxelization.frag b/projects/voxelization/assets/shaders/voxelization.frag similarity index 100% rename from projects/voxelization/resources/shaders/voxelization.frag rename to projects/voxelization/assets/shaders/voxelization.frag 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 100% rename from projects/voxelization/resources/shaders/voxelization.vert rename to projects/voxelization/assets/shaders/voxelization.vert 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/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/BloomAndFlares.cpp b/projects/voxelization/src/BloomAndFlares.cpp deleted file mode 100644 index fac57735a6544c197f880f78e1f512382607d048..0000000000000000000000000000000000000000 --- a/projects/voxelization/src/BloomAndFlares.cpp +++ /dev/null @@ -1,341 +0,0 @@ -#include "BloomAndFlares.hpp" -#include <vkcv/shader/GLSLCompiler.hpp> -#include <vkcv/asset/asset_loader.hpp> - -vkcv::Image loadLenseDirtTexture(vkcv::Core* corePtr) { - const auto texture = vkcv::asset::loadTexture("resources/lensDirt.jpg"); - vkcv::Image image = corePtr->createImage(vk::Format::eR8G8B8A8Unorm, texture.width, texture.height); - image.fill((void*)texture.data.data(), texture.data.size()); - return image; -} - -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 / 2), - m_Height(height / 2), - m_LinearSampler(p_Core->createSampler(vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerMipmapMode::LINEAR, - vkcv::SamplerAddressMode::CLAMP_TO_EDGE)), - m_RadialLutSampler(p_Core->createSampler(vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerMipmapMode::LINEAR, - vkcv::SamplerAddressMode::REPEAT)), - m_Blur(p_Core->createImage(colorBufferFormat, m_Width, m_Height, 1, true, true, false)), - m_LensFeatures(p_Core->createImage(colorBufferFormat, m_Width, m_Height, 1, true, true, false)), - m_radialLut(p_Core->createImage(vk::Format::eR8G8B8A8Unorm, 128, 10, 1)), - m_lensDirt(loadLenseDirtTexture(p_Core)) -{ - vkcv::shader::GLSLCompiler compiler; - - // DOWNSAMPLE - vkcv::ShaderProgram dsProg; - compiler.compile(vkcv::ShaderStage::COMPUTE, - "resources/shaders/bloomDownsample.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/bloomUpsample.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])); - } - for (uint32_t mipLevel = 0; mipLevel < m_LensFeatures.getMipCount(); mipLevel++) { - m_UpsampleLensFlareDescSets.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/bloomFlaresComposite.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 }); - - // radial LUT - const auto texture = vkcv::asset::loadTexture("resources/RadialLUT.png"); - - m_radialLut.fill((void*)texture.data.data(), texture.data.size()); -} - -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 < std::min((uint32_t)m_DownsampleDescSets.size(), m_Blur.getMipCount()); 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()); - - const uint32_t targetMip = 2; - const uint32_t mipLevel = std::min(targetMip, m_LensFeatures.getMipCount()); - - 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(), mipLevel)}; - 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 / std::exp2(mipLevel))), - static_cast<uint32_t>(glm::ceil(dispatchCountY / std::exp2(mipLevel))), - 1 - }; - p_Core->recordComputeDispatchToCmdStream( - cmdStream, - m_LensFlarePipe, - lensFeatureDispatchCount, - {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_LensFlareDescSet).vulkanHandle)}, - vkcv::PushConstantData(nullptr, 0)); - - // upsample dispatch - p_Core->prepareImageForStorage(cmdStream, m_LensFeatures.getHandle()); - - // upsample dispatch for each mip map - for (uint32_t i = mipLevel; i > 0; i--) - { - // mip descriptor writes - vkcv::DescriptorWrites mipUpsampleWrites; - mipUpsampleWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(0, m_LensFeatures.getHandle(), i, true) }; - mipUpsampleWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(1, m_LinearSampler) }; - mipUpsampleWrites.storageImageWrites = { vkcv::StorageImageDescriptorWrite(2, m_LensFeatures.getHandle(), i - 1) }; - p_Core->writeDescriptorSet(m_UpsampleLensFlareDescSets[i], mipUpsampleWrites); - - auto mipDivisor = glm::pow(2.0f, static_cast<float>(i) - 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_UpsampleLensFlareDescSets[i]).vulkanHandle) }, - vkcv::PushConstantData(nullptr, 0) - ); - // image barrier between mips - p_Core->recordImageMemoryBarrier(cmdStream, m_LensFeatures.getHandle()); - } -} - -void BloomAndFlares::execCompositePipe(const vkcv::CommandStreamHandle &cmdStream, const vkcv::ImageHandle& colorAttachment, - const uint32_t attachmentWidth, const uint32_t attachmentHeight, const glm::vec3& cameraForward) -{ - 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()), - vkcv::SampledImageDescriptorWrite(4, m_radialLut.getHandle()), - vkcv::SampledImageDescriptorWrite(6, m_lensDirt.getHandle()) }; - compositeWrites.samplerWrites = {vkcv::SamplerDescriptorWrite(2, m_LinearSampler), - vkcv::SamplerDescriptorWrite(5, m_RadialLutSampler) }; - compositeWrites.storageImageWrites = {vkcv::StorageImageDescriptorWrite(3, colorAttachment)}; - p_Core->writeDescriptorSet(m_CompositeDescSet, compositeWrites); - - float dispatchCountX = static_cast<float>(attachmentWidth) / 8.0f; - float dispatchCountY = static_cast<float>(attachmentHeight) / 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((void*)&cameraForward, sizeof(cameraForward))); -} - -void BloomAndFlares::execWholePipeline(const vkcv::CommandStreamHandle &cmdStream, const vkcv::ImageHandle &colorAttachment, - const uint32_t attachmentWidth, const uint32_t attachmentHeight, const glm::vec3& cameraForward) -{ - execDownsamplePipe(cmdStream, colorAttachment); - execUpsamplePipe(cmdStream); - execLensFeaturePipe(cmdStream); - execCompositePipe(cmdStream, colorAttachment, attachmentWidth, attachmentHeight, cameraForward); -} - -void BloomAndFlares::updateImageDimensions(uint32_t width, uint32_t height) -{ - m_Width = width / 2; - m_Height = height / 2; - - 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, true, true, false); -} - - diff --git a/projects/voxelization/src/BloomAndFlares.hpp b/projects/voxelization/src/BloomAndFlares.hpp deleted file mode 100644 index 2b410e5b256c5820d908372d2e23fd495853274a..0000000000000000000000000000000000000000 --- a/projects/voxelization/src/BloomAndFlares.hpp +++ /dev/null @@ -1,53 +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, - const uint32_t attachmentWidth, const uint32_t attachmentHeight, const glm::vec3& cameraForward); - - 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::SamplerHandle m_RadialLutSampler; - vkcv::Image m_Blur; - vkcv::Image m_LensFeatures; - - vkcv::Image m_radialLut; - vkcv::Image m_lensDirt; - - vkcv::PipelineHandle m_DownsamplePipe; - std::vector<vkcv::DescriptorSetHandle> m_DownsampleDescSets; // per mip desc set - std::vector<vkcv::DescriptorSetHandle> m_UpsampleLensFlareDescSets; // 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, - const uint32_t attachmentWidth, const uint32_t attachmentHeight, const glm::vec3& cameraForward); -}; - - - diff --git a/projects/voxelization/src/ShadowMapping.cpp b/projects/voxelization/src/ShadowMapping.cpp index a330394b7bd7ff2a4b8c347bd79e676dbc70f846..73f98cd3ea35d7f2bdf99d29f803df0abbc1adac 100644 --- a/projects/voxelization/src/ShadowMapping.cpp +++ b/projects/voxelization/src/ShadowMapping.cpp @@ -1,4 +1,6 @@ #include "ShadowMapping.hpp" + +#include <vkcv/Pass.hpp> #include <vkcv/shader/GLSLCompiler.hpp> const vk::Format shadowMapFormat = vk::Format::eR16G16B16A16Unorm; @@ -9,11 +11,11 @@ const vkcv::Multisampling msaa = vkcv::Multisampling::MSAA8 vkcv::ShaderProgram loadShadowShader() { vkcv::ShaderProgram shader; vkcv::shader::GLSLCompiler compiler; - compiler.compile(vkcv::ShaderStage::VERTEX, "resources/shaders/shadow.vert", + 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, "resources/shaders/shadow.frag", + compiler.compile(vkcv::ShaderStage::FRAGMENT, "assets/shaders/shadow.frag", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { shader.addShader(shaderStage, path); }); @@ -23,7 +25,7 @@ vkcv::ShaderProgram loadShadowShader() { vkcv::ShaderProgram loadDepthToMomentsShader() { vkcv::ShaderProgram shader; vkcv::shader::GLSLCompiler compiler; - compiler.compile(vkcv::ShaderStage::COMPUTE, "resources/shaders/depthToMoments.comp", + compiler.compile(vkcv::ShaderStage::COMPUTE, "assets/shaders/depthToMoments.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { shader.addShader(shaderStage, path); }); @@ -33,7 +35,7 @@ vkcv::ShaderProgram loadDepthToMomentsShader() { vkcv::ShaderProgram loadShadowBlurXShader() { vkcv::ShaderProgram shader; vkcv::shader::GLSLCompiler compiler; - compiler.compile(vkcv::ShaderStage::COMPUTE, "resources/shaders/shadowBlurX.comp", + compiler.compile(vkcv::ShaderStage::COMPUTE, "assets/shaders/shadowBlurX.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { shader.addShader(shaderStage, path); }); @@ -43,7 +45,7 @@ vkcv::ShaderProgram loadShadowBlurXShader() { vkcv::ShaderProgram loadShadowBlurYShader() { vkcv::ShaderProgram shader; vkcv::shader::GLSLCompiler compiler; - compiler.compile(vkcv::ShaderStage::COMPUTE, "resources/shaders/shadowBlurY.comp", + compiler.compile(vkcv::ShaderStage::COMPUTE, "assets/shaders/shadowBlurY.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { shader.addShader(shaderStage, path); }); @@ -151,34 +153,28 @@ glm::mat4 computeShadowViewProjectionMatrix( ShadowMapping::ShadowMapping(vkcv::Core* corePtr, const vkcv::VertexLayout& vertexLayout) : m_corePtr(corePtr), - m_shadowMap(corePtr->createImage(shadowMapFormat, shadowMapResolution, shadowMapResolution, 1, true, true)), - m_shadowMapIntermediate(corePtr->createImage(shadowMapFormat, shadowMapResolution, shadowMapResolution, 1, false, true)), - m_shadowMapDepth(corePtr->createImage(shadowMapDepthFormat, shadowMapResolution, shadowMapResolution, 1, false, false, false, msaa)), - m_lightInfoBuffer(corePtr->createBuffer<LightInfo>(vkcv::BufferType::UNIFORM, sizeof(glm::vec3))){ + 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 - const std::vector<vkcv::AttachmentDescription> shadowAttachments = { - vkcv::AttachmentDescription(vkcv::AttachmentOperation::STORE, vkcv::AttachmentOperation::CLEAR, shadowMapDepthFormat) - }; - vkcv::PassConfig shadowPassConfig(shadowAttachments, msaa); - m_shadowMapPass = corePtr->createPass(shadowPassConfig); + m_shadowMapPass = vkcv::passFormat(*corePtr, shadowMapDepthFormat, true, msaa); // pipeline - vkcv::PipelineConfig shadowPipeConfig{ + vkcv::GraphicsPipelineConfig shadowPipeConfig ( shadowShader, - shadowMapResolution, - shadowMapResolution, m_shadowMapPass, vertexLayout, - {}, - false - }; - shadowPipeConfig.m_multisampling = msaa; - shadowPipeConfig.m_EnableDepthClamping = true; - shadowPipeConfig.m_culling = vkcv::CullMode::Front; - m_shadowMapPipe = corePtr->createGraphicsPipeline(shadowPipeConfig); + {} + ); + + shadowPipeConfig.setResolution(shadowMapResolution, shadowMapResolution); + shadowPipeConfig.setDepthClampingEnabled(true); + shadowPipeConfig.setCulling(vkcv::CullMode::Front); + m_shadowMapPipe = corePtr->createGraphicsPipeline(shadowPipeConfig); m_shadowSampler = corePtr->createSampler( vkcv::SamplerFilterType::LINEAR, @@ -189,49 +185,55 @@ ShadowMapping::ShadowMapping(vkcv::Core* corePtr, const vkcv::VertexLayout& vert // depth to moments vkcv::ShaderProgram depthToMomentsShader = loadDepthToMomentsShader(); - m_depthToMomentsDescriptorSet = corePtr->createDescriptorSet(depthToMomentsShader.getReflectedDescriptors()[0]); - m_depthToMomentsPipe = corePtr->createComputePipeline(depthToMomentsShader, { corePtr->getDescriptorSet(m_depthToMomentsDescriptorSet).layout }); + + 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.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(0, m_shadowMapDepth.getHandle()) }; - depthToMomentDescriptorWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(1, m_shadowSampler) }; - depthToMomentDescriptorWrites.storageImageWrites = { vkcv::StorageImageDescriptorWrite(2, m_shadowMap.getHandle()) }; + 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_shadowBlurXDescriptorSet = corePtr->createDescriptorSet(shadowBlurXShader.getReflectedDescriptors()[0]); - m_shadowBlurXPipe = corePtr->createComputePipeline(shadowBlurXShader, { corePtr->getDescriptorSet(m_shadowBlurXDescriptorSet).layout }); + 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.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(0, m_shadowMap.getHandle()) }; - shadowBlurXDescriptorWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(1, m_shadowSampler) }; - shadowBlurXDescriptorWrites.storageImageWrites = { vkcv::StorageImageDescriptorWrite(2, m_shadowMapIntermediate.getHandle()) }; + 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_shadowBlurYDescriptorSet = corePtr->createDescriptorSet(shadowBlurYShader.getReflectedDescriptors()[0]); - m_shadowBlurYPipe = corePtr->createComputePipeline(shadowBlurYShader, { corePtr->getDescriptorSet(m_shadowBlurYDescriptorSet).layout }); - - vkcv::DescriptorWrites shadowBlurYDescriptorWrites; - shadowBlurYDescriptorWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(0, m_shadowMapIntermediate.getHandle()) }; - shadowBlurYDescriptorWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(1, m_shadowSampler) }; - shadowBlurYDescriptorWrites.storageImageWrites = { vkcv::StorageImageDescriptorWrite(2, m_shadowMap.getHandle()) }; + 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::Mesh>& meshes, - const std::vector<glm::mat4>& modelMatrices, - const vkcv::camera::Camera& camera, - const glm::vec3& voxelVolumeOffset, - float voxelVolumeExtent) { + 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; @@ -248,43 +250,55 @@ void ShadowMapping::recordShadowMapRendering( voxelVolumeOffset, voxelVolumeExtent); m_lightInfoBuffer.fill({ lightInfo }); - - std::vector<glm::mat4> mvpLight; + + vkcv::PushConstants shadowPushConstants = vkcv::pushConstants<glm::mat4>(); + for (const auto& m : modelMatrices) { - mvpLight.push_back(lightInfo.lightMatrix * m); + shadowPushConstants.appendDrawcall(lightInfo.lightMatrix * m); } - const vkcv::PushConstantData shadowPushConstantData((void*)mvpLight.data(), sizeof(glm::mat4)); - - std::vector<vkcv::DrawcallInfo> drawcalls; + + std::vector<vkcv::InstanceDrawcall> drawcalls; for (const auto& mesh : meshes) { - drawcalls.push_back(vkcv::DrawcallInfo(mesh, {})); + drawcalls.push_back(vkcv::InstanceDrawcall(mesh)); } + m_corePtr->recordBeginDebugLabel(cmdStream, "Shadow map depth", {1, 1, 1, 1}); m_corePtr->recordDrawcallsToCmdStream( cmdStream, - m_shadowMapPass, m_shadowMapPipe, - shadowPushConstantData, + shadowPushConstants, drawcalls, - { m_shadowMapDepth.getHandle() }); + { m_shadowMapDepth.getHandle() }, + windowHandle + ); m_corePtr->prepareImageForSampling(cmdStream, m_shadowMapDepth.getHandle()); + m_corePtr->recordEndDebugLabel(cmdStream); // depth to moments - uint32_t dispatchCount[3]; - dispatchCount[0] = static_cast<uint32_t>(std::ceil(shadowMapResolution / 8.f)); - dispatchCount[1] = static_cast<uint32_t>(std::ceil(shadowMapResolution / 8.f)); - dispatchCount[2] = 1; + const auto dispatchCount = vkcv::dispatchInvocations( + vkcv::DispatchSize(shadowMapResolution, shadowMapResolution), + vkcv::DispatchSize(8, 8) + ); - const uint32_t msaaSampleCount = msaaToSampleCount(msaa); + 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::DescriptorSetUsage(0, m_corePtr->getDescriptorSet(m_depthToMomentsDescriptorSet).vulkanHandle) }, - vkcv::PushConstantData((void*)&msaaSampleCount, sizeof(msaaSampleCount))); + { 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()); @@ -292,8 +306,9 @@ void ShadowMapping::recordShadowMapRendering( cmdStream, m_shadowBlurXPipe, dispatchCount, - { vkcv::DescriptorSetUsage(0, m_corePtr->getDescriptorSet(m_shadowBlurXDescriptorSet).vulkanHandle) }, - vkcv::PushConstantData(nullptr, 0)); + { vkcv::useDescriptorSet(0, m_shadowBlurXDescriptorSet) }, + vkcv::PushConstants(0) + ); m_corePtr->prepareImageForSampling(cmdStream, m_shadowMapIntermediate.getHandle()); // blur Y @@ -302,10 +317,12 @@ void ShadowMapping::recordShadowMapRendering( cmdStream, m_shadowBlurYPipe, dispatchCount, - { vkcv::DescriptorSetUsage(0, m_corePtr->getDescriptorSet(m_shadowBlurYDescriptorSet).vulkanHandle) }, - vkcv::PushConstantData(nullptr, 0)); - m_shadowMap.recordMipChainGeneration(cmdStream); - m_corePtr->prepareImageForSampling(cmdStream, m_shadowMap.getHandle()); + { vkcv::useDescriptorSet(0, m_shadowBlurYDescriptorSet) }, + vkcv::PushConstants(0) + ); + m_shadowMap.recordMipChainGeneration(cmdStream, downsampler); + + m_corePtr->recordEndDebugLabel(cmdStream); } vkcv::ImageHandle ShadowMapping::getShadowMap() { diff --git a/projects/voxelization/src/ShadowMapping.hpp b/projects/voxelization/src/ShadowMapping.hpp index 8066d5bdc90a66c0823be4dc23cf6a12729e32c7..a843a7de9c9e4c731b16ae0c8d5bdfd2ac263711 100644 --- a/projects/voxelization/src/ShadowMapping.hpp +++ b/projects/voxelization/src/ShadowMapping.hpp @@ -1,8 +1,13 @@ #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 { @@ -18,16 +23,18 @@ 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::Mesh>& meshes, - const std::vector<glm::mat4>& modelMatrices, - const vkcv::camera::Camera& camera, - const glm::vec3& voxelVolumeOffset, - float voxelVolumeExtent); + 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(); @@ -36,21 +43,24 @@ public: 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::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::PipelineHandle m_shadowMapPipe; + vkcv::PassHandle m_shadowMapPass; + vkcv::GraphicsPipelineHandle m_shadowMapPipe; - vkcv::PipelineHandle m_depthToMomentsPipe; - vkcv::DescriptorSetHandle m_depthToMomentsDescriptorSet; + vkcv::ComputePipelineHandle m_depthToMomentsPipe; + vkcv::DescriptorSetLayoutHandle m_depthToMomentsDescriptorSetLayout; + vkcv::DescriptorSetHandle m_depthToMomentsDescriptorSet; - vkcv::PipelineHandle m_shadowBlurXPipe; - vkcv::DescriptorSetHandle m_shadowBlurXDescriptorSet; + vkcv::ComputePipelineHandle m_shadowBlurXPipe; + vkcv::DescriptorSetLayoutHandle m_shadowBlurXDescriptorSetLayout; + vkcv::DescriptorSetHandle m_shadowBlurXDescriptorSet; - vkcv::PipelineHandle m_shadowBlurYPipe; - vkcv::DescriptorSetHandle m_shadowBlurYDescriptorSet; + 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 c117b4b9e6b896fbf51aae83343f30281061be9f..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,7 +54,7 @@ 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); }); @@ -62,7 +64,7 @@ vkcv::ShaderProgram loadVoxelBufferToImageShader() { vkcv::ShaderProgram loadSecondaryBounceShader() { vkcv::shader::GLSLCompiler compiler; vkcv::ShaderProgram shader; - compiler.compile(vkcv::ShaderStage::COMPUTE, "resources/shaders/voxelSecondaryBounce.comp", + compiler.compile(vkcv::ShaderStage::COMPUTE, "assets/shaders/voxelSecondaryBounce.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { shader.addShader(shaderStage, path); }); @@ -83,141 +85,138 @@ Voxelization::Voxelization( vkcv::SamplerHandle voxelSampler, vkcv::Multisampling msaa) : - m_corePtr(corePtr), - m_voxelImage(m_corePtr->createImage(vk::Format::eR16G16B16A16Sfloat, voxelResolution, voxelResolution, voxelResolution, true, true)), - m_voxelImageIntermediate(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_voxelImageIntermediate.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 }); - voxelVisualisationPassDefinition.msaa = msaa; - m_visualisationPass = m_corePtr->createPass(voxelVisualisationPassDefinition); - - 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 - voxelVisualisationPipeConfig.m_multisampling = msaa; + { 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_voxelImageIntermediate.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_secondaryBounceDescriptorSet = m_corePtr->createDescriptorSet(secondaryBounceShader.getReflectedDescriptors()[0]); - m_secondaryBouncePipe = m_corePtr->createComputePipeline( + m_secondaryBounceDescriptorSetLayout = m_corePtr->createDescriptorSetLayout(secondaryBounceShader.getReflectedDescriptors().at(0)); + m_secondaryBounceDescriptorSet = m_corePtr->createDescriptorSet(m_secondaryBounceDescriptorSetLayout); + m_secondaryBouncePipe = m_corePtr->createComputePipeline({ secondaryBounceShader, - { m_corePtr->getDescriptorSet(m_secondaryBounceDescriptorSet).layout }); + { m_secondaryBounceDescriptorSetLayout } + }); vkcv::DescriptorWrites secondaryBounceDescriptorWrites; - secondaryBounceDescriptorWrites.storageBufferWrites = { vkcv::StorageBufferDescriptorWrite(0, m_voxelBuffer.getHandle()) }; - secondaryBounceDescriptorWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(1, m_voxelImageIntermediate.getHandle()) }; - secondaryBounceDescriptorWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(2, voxelSampler) }; - secondaryBounceDescriptorWrites.storageImageWrites = { vkcv::StorageImageDescriptorWrite(3, m_voxelImage.getHandle()) }; - secondaryBounceDescriptorWrites.uniformBufferWrites = { vkcv::UniformBufferDescriptorWrite(4, m_voxelInfoBuffer.getHandle()) }; + 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 std::vector<vkcv::Mesh>& meshes, + 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) { m_voxelInfoBuffer.fill({ m_voxelInfo }); @@ -232,119 +231,129 @@ void Voxelization::voxelizeMeshes( 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->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) - },1)); + 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_voxelImageIntermediate.getHandle()); + m_corePtr->recordEndDebugLabel(cmdStream); // intermediate image mipchain - m_voxelImageIntermediate.recordMipChainGeneration(cmdStream); - m_corePtr->prepareImageForSampling(cmdStream, m_voxelImageIntermediate.getHandle()); + 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::DescriptorSetUsage(0, m_corePtr->getDescriptorSet(m_secondaryBounceDescriptorSet).vulkanHandle) }, - vkcv::PushConstantData(nullptr, 0)); - m_voxelImage.recordMipChainGeneration(cmdStream); - - m_corePtr->recordImageMemoryBarrier(cmdStream, m_voxelImage.getHandle()); + { vkcv::useDescriptorSet(0, m_secondaryBounceDescriptorSet) }, + vkcv::PushConstants(0)); + m_voxelImage.recordMipChainGeneration(cmdStream, downsampler); + m_corePtr->recordEndDebugLabel(cmdStream); // final image mipchain - m_voxelImage.recordMipChainGeneration(cmdStream); - m_corePtr->prepareImageForSampling(cmdStream, m_voxelImage.getHandle()); + 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) },1); - + + 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) { diff --git a/projects/voxelization/src/Voxelization.hpp b/projects/voxelization/src/Voxelization.hpp index 66c87acb3c13c0d950a28dc33e4084d728da5947..f624d03503b67cc2de0632367d90742fb975473e 100644 --- a/projects/voxelization/src/Voxelization.hpp +++ b/projects/voxelization/src/Voxelization.hpp @@ -1,7 +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: @@ -21,15 +25,18 @@ public: void voxelizeMeshes( vkcv::CommandStreamHandle cmdStream, - const std::vector<vkcv::Mesh>& meshes, + 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); @@ -53,24 +60,29 @@ private: vkcv::Image m_voxelImage; vkcv::Buffer<VoxelBufferContent> m_voxelBuffer; - vkcv::Image m_dummyRenderTarget; - vkcv::PassHandle m_voxelizationPass; - vkcv::PipelineHandle m_voxelizationPipe; - vkcv::DescriptorSetHandle m_voxelizationDescriptorSet; + vkcv::Image m_dummyRenderTarget; + vkcv::PassHandle m_voxelizationPass; + vkcv::GraphicsPipelineHandle m_voxelizationPipe; + vkcv::DescriptorSetLayoutHandle m_voxelizationDescriptorSetLayout; + vkcv::DescriptorSetHandle m_voxelizationDescriptorSet; - vkcv::PipelineHandle m_voxelResetPipe; - vkcv::DescriptorSetHandle m_voxelResetDescriptorSet; + vkcv::ComputePipelineHandle m_voxelResetPipe; + vkcv::DescriptorSetLayoutHandle m_voxelResetDescriptorSetLayout; + vkcv::DescriptorSetHandle m_voxelResetDescriptorSet; - vkcv::PipelineHandle m_bufferToImagePipe; - vkcv::DescriptorSetHandle m_bufferToImageDescriptorSet; + vkcv::ComputePipelineHandle m_bufferToImagePipe; + vkcv::DescriptorSetLayoutHandle m_bufferToImageDescriptorSetLayout; + vkcv::DescriptorSetHandle m_bufferToImageDescriptorSet; - vkcv::PassHandle m_visualisationPass; - vkcv::PipelineHandle m_visualisationPipe; + vkcv::PassHandle m_visualisationPass; + vkcv::GraphicsPipelineHandle m_visualisationPipe; - vkcv::PipelineHandle m_secondaryBouncePipe; - vkcv::DescriptorSetHandle m_secondaryBounceDescriptorSet; + 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; diff --git a/projects/voxelization/src/main.cpp b/projects/voxelization/src/main.cpp index edc50c554b6c73bd2f06914eba6dd7adf9e43483..ac2b2b6a19c23e039178da43705a4f1033fa6682 100644 --- a/projects/voxelization/src/main.cpp +++ b/projects/voxelization/src/main.cpp @@ -1,37 +1,76 @@ #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 "BloomAndFlares.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.requireExtensionFeature<vk::PhysicalDeviceDescriptorIndexingFeatures>( + VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME, + [](vk::PhysicalDeviceDescriptorIndexingFeatures& features) { + features.setDescriptorBindingPartiallyBound(true); + } + ); - vkcv::Window window = vkcv::Window::create( - applicationName, - windowWidth, - windowHeight, - true + 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); + } ); - bool isFullscreen = false; - int windowedWidthBackup = windowWidth; - int windowedHeightBackup = windowHeight; - int windowedPosXBackup; - int windowedPosYBackup; + 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( + 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) { @@ -47,8 +86,8 @@ int main(int argc, const char** argv) { GLFW_DONT_CARE); } else { - windowedWidthBackup = windowWidth; - windowedHeightBackup = windowHeight; + windowedWidthBackup = window.getWidth(); + windowedHeightBackup = window.getHeight(); glfwGetWindowPos(window.getWindow(), &windowedPosXBackup, &windowedPosYBackup); @@ -69,28 +108,19 @@ int main(int argc, const char** argv) { }); vkcv::camera::CameraManager cameraManager(window); - uint32_t camIndex = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); - uint32_t camIndex2 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); + auto camHandle0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); + auto camHandle1 = 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(camIndex).setFov(glm::radians(37.8)); // fov of a 35mm lens + 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(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" } - ); + 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); @@ -107,25 +137,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); @@ -133,35 +155,38 @@ 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::LOAD, - depthBufferFormat + const vkcv::AttachmentDescription depth_attachment ( + depthBufferFormat, + vkcv::AttachmentOperation::LOAD, + vkcv::AttachmentOperation::STORE ); // forward shading config @@ -171,11 +196,11 @@ int main(int argc, const char** argv) { 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); }); @@ -184,20 +209,20 @@ 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); + 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); // depth prepass config vkcv::ShaderProgram depthPrepassShader; - compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("resources/shaders/depthPrepass.vert"), + 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("resources/shaders/depthPrepass.frag"), + compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("assets/shaders/depthPrepass.frag"), [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { depthPrepassShader.addShader(shaderStage, path); }); @@ -206,28 +231,22 @@ int main(int argc, const char** argv) { std::vector<vkcv::VertexBinding> prepassVertexBindings; for (size_t i = 0; i < prepassVertexAttachments.size(); i++) { - prepassVertexBindings.push_back(vkcv::VertexBinding(i, { prepassVertexAttachments[i] })); + prepassVertexBindings.push_back(vkcv::createVertexBinding(i, { prepassVertexAttachments[i] })); } - const vkcv::VertexLayout prepassVertexLayout(prepassVertexBindings); - - const vkcv::AttachmentDescription prepassAttachment( - vkcv::AttachmentOperation::STORE, - vkcv::AttachmentOperation::CLEAR, - depthBufferFormat); - - vkcv::PassConfig prepassPassDefinition({ prepassAttachment }, msaa); - vkcv::PassHandle prepassPass = core.createPass(prepassPassDefinition); + const vkcv::VertexLayout prepassVertexLayout { prepassVertexBindings }; + + vkcv::PassHandle prepassPass = vkcv::passFormat(core, depthBufferFormat, true, msaa); // create descriptor sets - vkcv::SamplerHandle colorSampler = core.createSampler( - vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerMipmapMode::LINEAR, - vkcv::SamplerAddressMode::REPEAT - ); + 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 albedoIndex = material.baseColor; @@ -247,88 +266,85 @@ int main(int argc, const char** argv) { specularIndex = 0; } - materialDescriptorSets.push_back(core.createDescriptorSet(forwardProgram.getReflectedDescriptors()[1])); + 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]; // albedo texture - sceneImages.push_back(core.createImage(vk::Format::eR8G8B8A8Srgb, albedoTexture.w, albedoTexture.h, 1, true)); + sceneImages.push_back(vkcv::image(core, vk::Format::eR8G8B8A8Srgb, albedoTexture.w, albedoTexture.h, 1, true)); sceneImages.back().fill(albedoTexture.data.data()); - sceneImages.back().generateMipChainImmediate(); - sceneImages.back().switchLayout(vk::ImageLayout::eShaderReadOnlyOptimal); + sceneImages.back().recordMipChainGeneration(mipStream, spdDownsampler); const vkcv::ImageHandle albedoHandle = sceneImages.back().getHandle(); // normal texture - sceneImages.push_back(core.createImage(vk::Format::eR8G8B8A8Unorm, normalTexture.w, normalTexture.h, 1, true)); + sceneImages.push_back(vkcv::image(core, vk::Format::eR8G8B8A8Unorm, normalTexture.w, normalTexture.h, 1, true, true)); sceneImages.back().fill(normalTexture.data.data()); - sceneImages.back().generateMipChainImmediate(); - sceneImages.back().switchLayout(vk::ImageLayout::eShaderReadOnlyOptimal); + sceneImages.back().recordMipChainGeneration(mipStream, spdDownsampler); const vkcv::ImageHandle normalHandle = sceneImages.back().getHandle(); // specular texture - sceneImages.push_back(core.createImage(vk::Format::eR8G8B8A8Unorm, specularTexture.w, specularTexture.h, 1, true)); + sceneImages.push_back(vkcv::image(core, vk::Format::eR8G8B8A8Unorm, specularTexture.w, specularTexture.h, 1, true, true)); sceneImages.back().fill(specularTexture.data.data()); - sceneImages.back().generateMipChainImmediate(); - sceneImages.back().switchLayout(vk::ImageLayout::eShaderReadOnlyOptimal); + sceneImages.back().recordMipChainGeneration(mipStream, spdDownsampler); const vkcv::ImageHandle specularHandle = sceneImages.back().getHandle(); vkcv::DescriptorWrites setWrites; - setWrites.sampledImageWrites = { - vkcv::SampledImageDescriptorWrite(0, albedoHandle), - vkcv::SampledImageDescriptorWrite(2, normalHandle), - vkcv::SampledImageDescriptorWrite(3, specularHandle) - }; - 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]); } // prepass pipeline - vkcv::DescriptorSetHandle prepassDescriptorSet = core.createDescriptorSet(std::vector<vkcv::DescriptorBinding>()); + vkcv::DescriptorSetLayoutHandle prepassDescriptorSetLayout = core.createDescriptorSetLayout({}); + vkcv::DescriptorSetHandle prepassDescriptorSet = core.createDescriptorSet(prepassDescriptorSetLayout); - vkcv::PipelineConfig prepassPipelineConfig{ + auto swapchainExtent = core.getSwapchainExtent(window.getSwapchain()); + + vkcv::GraphicsPipelineConfig prepassPipelineConfig ( depthPrepassShader, - windowWidth, - windowHeight, prepassPass, vertexLayout, - { - core.getDescriptorSet(prepassDescriptorSet).layout, - core.getDescriptorSet(perMeshDescriptorSets[0]).layout }, - true }; - prepassPipelineConfig.m_culling = vkcv::CullMode::Back; - prepassPipelineConfig.m_multisampling = msaa; - prepassPipelineConfig.m_depthTest = vkcv::DepthTest::LessEqual; - prepassPipelineConfig.m_alphaToCoverage = true; + { prepassDescriptorSetLayout, perMeshDescriptorSetLayouts[0] } + ); + + prepassPipelineConfig.setCulling(vkcv::CullMode::Back); + prepassPipelineConfig.setDepthTest(vkcv::DepthTest::LessEqual); + prepassPipelineConfig.setWritingAlphaToCoverage(true); - vkcv::PipelineHandle prepassPipeline = core.createGraphicsPipeline(prepassPipelineConfig); + vkcv::GraphicsPipelineHandle prepassPipeline = core.createGraphicsPipeline(prepassPipelineConfig); // forward pipeline - vkcv::PipelineConfig forwardPipelineConfig { + vkcv::GraphicsPipelineConfig forwardPipelineConfig ( forwardProgram, - windowWidth, - windowHeight, forwardPass, vertexLayout, - { - core.getDescriptorSet(forwardShadingDescriptorSet).layout, - core.getDescriptorSet(perMeshDescriptorSets[0]).layout }, - true - }; - forwardPipelineConfig.m_culling = vkcv::CullMode::Back; - forwardPipelineConfig.m_multisampling = msaa; - forwardPipelineConfig.m_depthTest = vkcv::DepthTest::Equal; - forwardPipelineConfig.m_depthWrite = false; + { 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; @@ -343,56 +359,77 @@ int main(int argc, const char** argv) { SkySettings skySettings; skySettings.color = glm::vec3(0.15, 0.65, 1); skySettings.strength = 5; - - const vkcv::AttachmentDescription skyColorAttachment( - vkcv::AttachmentOperation::STORE, - vkcv::AttachmentOperation::LOAD, - colorBufferFormat); - - const vkcv::AttachmentDescription skyDepthAttachments( - vkcv::AttachmentOperation::STORE, - vkcv::AttachmentOperation::LOAD, - depthBufferFormat); - - vkcv::PassConfig skyPassConfig({ skyColorAttachment, skyDepthAttachments }, msaa); - vkcv::PassHandle skyPass = core.createPass(skyPassConfig); + + vkcv::PassHandle skyPass = vkcv::passFormats( + core, + { colorBufferFormat, depthBufferFormat }, + false, + msaa + ); vkcv::ShaderProgram skyShader; - compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("resources/shaders/sky.vert"), + compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("assets/shaders/sky.vert"), [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { skyShader.addShader(shaderStage, path); }); - compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/sky.frag"), + compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("assets/shaders/sky.frag"), [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { skyShader.addShader(shaderStage, path); }); - vkcv::PipelineConfig skyPipeConfig; - skyPipeConfig.m_ShaderProgram = skyShader; - skyPipeConfig.m_Width = windowWidth; - skyPipeConfig.m_Height = windowHeight; - skyPipeConfig.m_PassHandle = skyPass; - skyPipeConfig.m_VertexLayout = vkcv::VertexLayout(); - skyPipeConfig.m_DescriptorLayouts = {}; - skyPipeConfig.m_UseDynamicViewport = true; - skyPipeConfig.m_multisampling = msaa; - skyPipeConfig.m_depthWrite = false; + vkcv::GraphicsPipelineConfig skyPipeConfig ( + skyShader, + skyPass, + {}, + {} + ); + + skyPipeConfig.setWritingDepth(false); - vkcv::PipelineHandle skyPipe = core.createGraphicsPipeline(skyPipeConfig); + vkcv::GraphicsPipelineHandle skyPipe = core.createGraphicsPipeline(skyPipeConfig); // render targets - vkcv::ImageHandle depthBuffer = core.createImage(depthBufferFormat, windowWidth, windowHeight, 1, false, false, false, msaa).getHandle(); + vkcv::ImageHandle depthBuffer = core.createImage( + depthBufferFormat, + swapchainExtent.width, + swapchainExtent.height, + 1, false, false, false, msaa + ); - const bool colorBufferRequiresStorage = !usingMsaa; - vkcv::ImageHandle colorBuffer = core.createImage(colorBufferFormat, windowWidth, windowHeight, 1, false, colorBufferRequiresStorage, true, msaa).getHandle(); + const bool colorBufferRequiresStorage = !usingMsaa; + vkcv::ImageHandle colorBuffer = core.createImage( + colorBufferFormat, + swapchainExtent.width, + swapchainExtent.height, + 1, false, colorBufferRequiresStorage, true, msaa + ); vkcv::ImageHandle resolvedColorBuffer; if (usingMsaa) { - resolvedColorBuffer = core.createImage(colorBufferFormat, windowWidth, windowHeight, 1, false, true, true).getHandle(); + resolvedColorBuffer = core.createImage( + colorBufferFormat, + swapchainExtent.width, + swapchainExtent.height, + 1, false, true, true + ); } else { resolvedColorBuffer = colorBuffer; } + + vkcv::ImageHandle swapBuffer = core.createImage( + colorBufferFormat, + swapchainExtent.width, + swapchainExtent.height, + 1, false, true + ); + + vkcv::ImageHandle swapBuffer2 = core.createImage( + colorBufferFormat, + swapchainExtent.width, + swapchainExtent.height, + 1, false, true + ); const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); @@ -412,33 +449,50 @@ int main(int argc, const char** argv) { // 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( + + vkcv::DescriptorSetLayoutHandle tonemappingDescriptorSetLayout = core.createDescriptorSetLayout( + tonemappingProgram.getReflectedDescriptors().at(0)); + vkcv::DescriptorSetHandle tonemappingDescriptorSet = core.createDescriptorSet(tonemappingDescriptorSetLayout); + vkcv::ComputePipelineHandle tonemappingPipeline = core.createComputePipeline({ tonemappingProgram, - { core.getDescriptorSet(tonemappingDescriptorSet).layout }); + { 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, "resources/shaders/msaa4XResolve.comp", + compiler.compile(vkcv::ShaderStage::COMPUTE, "assets/shaders/msaa4XResolve.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { resolveProgram.addShader(shaderStage, path); }); - vkcv::DescriptorSetHandle resolveDescriptorSet = core.createDescriptorSet( - resolveProgram.getReflectedDescriptors()[0]); - vkcv::PipelineHandle resolvePipeline = core.createComputePipeline( + + vkcv::DescriptorSetLayoutHandle resolveDescriptorSetLayout = core.createDescriptorSetLayout( + resolveProgram.getReflectedDescriptors().at(0)); + vkcv::DescriptorSetHandle resolveDescriptorSet = core.createDescriptorSet(resolveDescriptorSetLayout); + vkcv::ComputePipelineHandle resolvePipeline = core.createComputePipeline({ resolveProgram, - { core.getDescriptorSet(resolveDescriptorSet).layout }); + { resolveDescriptorSetLayout } + }); - vkcv::SamplerHandle resolveSampler = core.createSampler( - vkcv::SamplerFilterType::NEAREST, - vkcv::SamplerFilterType::NEAREST, - vkcv::SamplerMipmapMode::NEAREST, - vkcv::SamplerAddressMode::CLAMP_TO_EDGE); + vkcv::SamplerHandle resolveSampler = vkcv::samplerNearest(core, true); // model matrices per mesh std::vector<glm::mat4> modelMatrices; @@ -451,29 +505,30 @@ int main(int argc, const char** argv) { } // prepare meshes - 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); + 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> prepassDrawcalls; - 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) })); - prepassDrawcalls.push_back(vkcv::DrawcallInfo(meshes[i], { - vkcv::DescriptorSetUsage(0, core.getDescriptorSet(prepassDescriptorSet).vulkanHandle), - vkcv::DescriptorSetUsage(1, core.getDescriptorSet(perMeshDescriptorSets[i]).vulkanHandle) })); + 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 = core.createSampler( - vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerMipmapMode::LINEAR, - vkcv::SamplerAddressMode::CLAMP_TO_EDGE); + vkcv::SamplerHandle voxelSampler = vkcv::samplerLinear(core, true); ShadowMapping shadowMapping(&core, vertexLayout); @@ -490,15 +545,8 @@ int main(int argc, const char** argv) { voxelSampler, msaa); - BloomAndFlares bloomFlares(&core, colorBufferFormat, windowWidth, windowHeight); - - window.e_key.add([&](int key, int scancode, int action, int mods) { - if (key == GLFW_KEY_R && action == GLFW_PRESS) { - bloomFlares = BloomAndFlares(&core, colorBufferFormat, windowWidth, windowHeight); - } - }); - - vkcv::Buffer<glm::vec3> cameraPosBuffer = core.createBuffer<glm::vec3>(vkcv::BufferType::UNIFORM, 1); + vkcv::effects::BloomAndFlaresEffect bloomFlares (core, true); + vkcv::Buffer<glm::vec3> cameraPosBuffer = buffer<glm::vec3>(core, vkcv::BufferType::UNIFORM, 1); struct VolumetricSettings { glm::vec3 scatteringCoefficient; @@ -506,24 +554,63 @@ int main(int argc, const char** argv) { glm::vec3 absorptionCoefficient; }; vkcv::Buffer<VolumetricSettings> volumetricSettingsBuffer - = core.createBuffer<VolumetricSettings>(vkcv::BufferType::UNIFORM ,1); + = buffer<VolumetricSettings>(core, vkcv::BufferType::UNIFORM ,1); // write forward pass descriptor set vkcv::DescriptorWrites forwardDescriptorWrites; - forwardDescriptorWrites.uniformBufferWrites = { - vkcv::UniformBufferDescriptorWrite(0, shadowMapping.getLightInfoBuffer()), - vkcv::UniformBufferDescriptorWrite(3, cameraPosBuffer.getHandle()), - vkcv::UniformBufferDescriptorWrite(6, voxelization.getVoxelInfoBufferHandle()), - vkcv::UniformBufferDescriptorWrite(7, volumetricSettingsBuffer.getHandle())}; - forwardDescriptorWrites.sampledImageWrites = { - vkcv::SampledImageDescriptorWrite(1, shadowMapping.getShadowMap()), - vkcv::SampledImageDescriptorWrite(4, voxelization.getVoxelImageHandle()) }; - forwardDescriptorWrites.samplerWrites = { - vkcv::SamplerDescriptorWrite(2, shadowMapping.getShadowSampler()), - vkcv::SamplerDescriptorWrite(5, voxelSampler) }; + 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); - vkcv::gui::GUI gui(core, window); + 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); glm::vec2 lightAnglesDegree = glm::vec2(90.f, 0.f); glm::vec3 lightColor = glm::vec3(1); @@ -540,54 +627,96 @@ int main(int argc, const char** argv) { glm::vec3 absorptionColor = glm::vec3(1); float absorptionDensity = 0.005; float volumetricAmbient = 0.2; - - 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, 1, false, false, false, msaa).getHandle(); - colorBuffer = core.createImage(colorBufferFormat, swapchainWidth, swapchainHeight, 1, false, colorBufferRequiresStorage, true, msaa).getHandle(); + + 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); + } + + depthBuffer = core.createImage( + depthBufferFormat, + fsrWidth, fsrHeight, 1, + false, false, false, + msaa + ); + + colorBuffer = core.createImage( + colorBufferFormat, + fsrWidth, fsrHeight, 1, + false, colorBufferRequiresStorage, true, + msaa + ); if (usingMsaa) { - resolvedColorBuffer = core.createImage(colorBufferFormat, swapchainWidth, swapchainHeight, 1, false, true, true).getHandle(); - } - else { + resolvedColorBuffer = core.createImage( + colorBufferFormat, + fsrWidth, fsrHeight, 1, + false, true, true + ); + } else { resolvedColorBuffer = colorBuffer; } - - windowWidth = swapchainWidth; - windowHeight = swapchainHeight; - - bloomFlares.updateImageDimensions(windowWidth, windowHeight); + + swapBuffer = core.createImage( + colorBufferFormat, + fsrWidth, fsrHeight, 1, + false, true + ); + + swapBuffer2 = core.createImage( + colorBufferFormat, + swapchainWidth, swapchainHeight, 1, + false, true + ); } - 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.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(0, resolvedColorBuffer) }; - tonemappingDescriptorWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(1, colorSampler) }; - tonemappingDescriptorWrites.storageImageWrites = { vkcv::StorageImageDescriptorWrite(2, swapchainInput) }; + tonemappingDescriptorWrites.writeSampledImage(0, resolvedColorBuffer); + tonemappingDescriptorWrites.writeSampler(1, colorSampler); + tonemappingDescriptorWrites.writeStorageImage(2, swapBuffer); 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.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(0, colorBuffer) }; - resolveDescriptorWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(1, resolveSampler) }; - resolveDescriptorWrites.storageImageWrites = { vkcv::StorageImageDescriptorWrite(2, resolvedColorBuffer) }; + resolveDescriptorWrites.writeSampledImage(0, colorBuffer); + resolveDescriptorWrites.writeSampler(1, resolveSampler); + resolveDescriptorWrites.writeStorageImage(2, resolvedColorBuffer); core.writeDescriptorSet(resolveDescriptorSet, resolveDescriptorWrites); - start = end; - cameraManager.update(0.000001 * static_cast<double>(deltatime.count())); + cameraManager.update(dt); cameraPosBuffer.fill({ cameraManager.getActiveCamera().getPosition() }); auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); @@ -606,7 +735,10 @@ int main(int argc, const char** argv) { modelMatrices, cameraManager.getActiveCamera(), voxelization.getVoxelOffset(), - voxelization.getVoxelExtent()); + voxelization.getVoxelExtent(), + windowHandle, + spdDownsampler + ); // voxelization voxelization.setVoxelExtent(voxelizationExtent); @@ -614,33 +746,41 @@ int main(int argc, const char** argv) { cmdStream, meshes, modelMatrices, - perMeshDescriptorSets); + perMeshDescriptorSets, + windowHandle, + spdDownsampler + ); // depth prepass const glm::mat4 viewProjectionCamera = cameraManager.getActiveCamera().getMVP(); - + + vkcv::PushConstants prepassPushConstants = vkcv::pushConstants<glm::mat4>(); + std::vector<glm::mat4> prepassMatrices; for (const auto& m : modelMatrices) { - prepassMatrices.push_back(viewProjectionCamera * m); + prepassPushConstants.appendDrawcall(viewProjectionCamera * m); } - - const vkcv::PushConstantData prepassPushConstantData((void*)prepassMatrices.data(), sizeof(glm::mat4)); + const std::vector<vkcv::ImageHandle> prepassRenderTargets = { depthBuffer }; + core.recordBeginDebugLabel(cmdStream, "Depth prepass", { 1, 1, 1, 1 }); core.recordDrawcallsToCmdStream( cmdStream, - prepassPass, prepassPipeline, - prepassPushConstantData, + prepassPushConstants, prepassDrawcalls, - prepassRenderTargets); + prepassRenderTargets, + windowHandle + ); core.recordImageMemoryBarrier(cmdStream, depthBuffer); - + core.recordEndDebugLabel(cmdStream); + + vkcv::PushConstants pushConstants (2 * sizeof(glm::mat4)); + // main pass - std::vector<std::array<glm::mat4, 2>> mainPassMatrices; for (const auto& m : modelMatrices) { - mainPassMatrices.push_back({ viewProjectionCamera * m, m }); + pushConstants.appendDrawcall(std::array<glm::mat4, 2>{ viewProjectionCamera * m, m }); } VolumetricSettings volumeSettings; @@ -648,74 +788,124 @@ int main(int argc, const char** argv) { volumeSettings.absorptionCoefficient = absorptionColor * absorptionDensity; volumeSettings.ambientLight = volumetricAmbient; volumetricSettingsBuffer.fill({ volumeSettings }); - - const vkcv::PushConstantData pushConstantData((void*)mainPassMatrices.data(), 2 * sizeof(glm::mat4)); + const std::vector<vkcv::ImageHandle> renderTargets = { colorBuffer, depthBuffer }; + core.recordBeginDebugLabel(cmdStream, "Forward rendering", { 1, 1, 1, 1 }); core.recordDrawcallsToCmdStream( cmdStream, - forwardPass, forwardPipeline, - pushConstantData, + pushConstants, drawcalls, - renderTargets); + renderTargets, + windowHandle + ); + core.recordEndDebugLabel(cmdStream); if (renderVoxelVis) { - voxelization.renderVoxelVisualisation(cmdStream, viewProjectionCamera, renderTargets, voxelVisualisationMip); + voxelization.renderVoxelVisualisation(cmdStream, viewProjectionCamera, renderTargets, voxelVisualisationMip, windowHandle); } + + 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, - skyPass, skyPipe, - vkcv::PushConstantData((void*)&skySettings, sizeof(skySettings)), - { vkcv::DrawcallInfo(vkcv::Mesh({}, nullptr, 3), {}) }, - renderTargets); - + skySettingsPushConstants, + { vkcv::InstanceDrawcall(skyData) }, + renderTargets, + windowHandle + ); + core.recordEndDebugLabel(cmdStream); + const uint32_t fullscreenLocalGroupSize = 8; - const uint32_t fulsscreenDispatchCount[3] = { - static_cast<uint32_t>(glm::ceil(windowWidth / static_cast<float>(fullscreenLocalGroupSize))), - static_cast<uint32_t>(glm::ceil(windowHeight / static_cast<float>(fullscreenLocalGroupSize))), - 1 - }; + 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, - fulsscreenDispatchCount, - { vkcv::DescriptorSetUsage(0, core.getDescriptorSet(resolveDescriptorSet).vulkanHandle) }, - vkcv::PushConstantData(nullptr, 0)); + 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); - bloomFlares.execWholePipeline(cmdStream, resolvedColorBuffer, windowWidth, windowHeight, - glm::normalize(cameraManager.getActiveCamera().getFront())); - - core.prepareImageForStorage(cmdStream, swapchainInput); + core.prepareImageForStorage(cmdStream, swapBuffer); core.prepareImageForSampling(cmdStream, resolvedColorBuffer); - - auto timeSinceStart = std::chrono::duration_cast<std::chrono::microseconds>(end - appStartTime); - float timeF = static_cast<float>(timeSinceStart.count()) * 0.01; - + + 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.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, - fulsscreenDispatchCount, - { vkcv::DescriptorSetUsage(0, core.getDescriptorSet(tonemappingDescriptorSet).vulkanHandle) }, - vkcv::PushConstantData(&timeF, sizeof(timeF))); + cmdStream, + postEffectsPipeline, + fullscreenDispatchCount, + { vkcv::useDescriptorSet(0, postEffectsDescriptorSet) }, + timePushConstants + ); + core.recordEndDebugLabel(cmdStream); // present and end core.prepareSwapchainImageForPresent(cmdStream); @@ -743,26 +933,41 @@ int main(int argc, const char** argv) { 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("resources/shaders/shader.vert"), + 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("resources/shaders/shader.frag"), + 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.m_ShaderProgram = newForwardProgram; - vkcv::PipelineHandle newPipeline = core.createGraphicsPipeline(forwardPipelineConfig); + + forwardPipelineConfig.setShaderProgram(newForwardProgram); + vkcv::GraphicsPipelineHandle newPipeline = core.createGraphicsPipeline(forwardPipelineConfig); if (newPipeline) { forwardPipeline = newPipeline; @@ -771,13 +976,15 @@ int main(int argc, const char** argv) { if (ImGui::Button("Reload tonemapping")) { vkcv::ShaderProgram newProgram; - compiler.compile(vkcv::ShaderStage::COMPUTE, std::filesystem::path("resources/shaders/tonemapping.comp"), + 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::PipelineHandle newPipeline = core.createComputePipeline( + + vkcv::ComputePipelineHandle newPipeline = core.createComputePipeline({ newProgram, - { core.getDescriptorSet(tonemappingDescriptorSet).layout }); + { tonemappingDescriptorSetLayout } + }); if (newPipeline) { tonemappingPipeline = newPipeline; @@ -787,9 +994,7 @@ int main(int argc, const char** argv) { } gui.endGUI(); - - core.endFrame(); - } + }); return 0; } diff --git a/projects/wobble_bobble/.gitignore b/projects/wobble_bobble/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..7ed07ed409373206a95d74e6dbdc27a4cb391fea --- /dev/null +++ b/projects/wobble_bobble/.gitignore @@ -0,0 +1 @@ +wobble_bobble diff --git a/projects/wobble_bobble/CMakeLists.txt b/projects/wobble_bobble/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..6c90d5222859ccd829cbb5a63ea367cc47214fb3 --- /dev/null +++ b/projects/wobble_bobble/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.16) +project(wobble_bobble) + +# 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(wobble_bobble src/main.cpp) + +# including headers of dependencies and the VkCV framework +target_include_directories(wobble_bobble 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(wobble_bobble vkcv vkcv_camera vkcv_gui vkcv_shader_compiler) diff --git a/projects/wobble_bobble/shaders/.gitignore b/projects/wobble_bobble/shaders/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/projects/wobble_bobble/shaders/grid.frag b/projects/wobble_bobble/shaders/grid.frag new file mode 100644 index 0000000000000000000000000000000000000000..8eb2fbc2173bbddb5bdc44c52ce81d2e5d52d389 --- /dev/null +++ b/projects/wobble_bobble/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/wobble_bobble/shaders/grid.vert b/projects/wobble_bobble/shaders/grid.vert new file mode 100644 index 0000000000000000000000000000000000000000..54de3f3e1da43e3ea3d018e5c54003b83e055c4c --- /dev/null +++ b/projects/wobble_bobble/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/wobble_bobble/shaders/init_particle_weights.comp b/projects/wobble_bobble/shaders/init_particle_weights.comp new file mode 100644 index 0000000000000000000000000000000000000000..9b821e88fc7cc3fcea87b7eb6de0f8f6d629d911 --- /dev/null +++ b/projects/wobble_bobble/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/wobble_bobble/shaders/lines.frag b/projects/wobble_bobble/shaders/lines.frag new file mode 100644 index 0000000000000000000000000000000000000000..37d79d30d2ce751a59b1b467764b2fe464aa5c17 --- /dev/null +++ b/projects/wobble_bobble/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/wobble_bobble/shaders/lines.vert b/projects/wobble_bobble/shaders/lines.vert new file mode 100644 index 0000000000000000000000000000000000000000..b8e3b01c67986156ad980899697ffea05409b752 --- /dev/null +++ b/projects/wobble_bobble/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/wobble_bobble/shaders/particle.frag b/projects/wobble_bobble/shaders/particle.frag new file mode 100644 index 0000000000000000000000000000000000000000..81c0a3594359e816a28b5e3f0301b47ce74a3cd7 --- /dev/null +++ b/projects/wobble_bobble/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/wobble_bobble/shaders/particle.inc b/projects/wobble_bobble/shaders/particle.inc new file mode 100644 index 0000000000000000000000000000000000000000..a622485eab5bbcafdc51c030281e53b1cc1c5f11 --- /dev/null +++ b/projects/wobble_bobble/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/wobble_bobble/shaders/particle.vert b/projects/wobble_bobble/shaders/particle.vert new file mode 100644 index 0000000000000000000000000000000000000000..a8f697e79eacba361d80b5858405add675e761bb --- /dev/null +++ b/projects/wobble_bobble/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/wobble_bobble/shaders/transform_particles_to_grid.comp b/projects/wobble_bobble/shaders/transform_particles_to_grid.comp new file mode 100644 index 0000000000000000000000000000000000000000..1be18c41303ab2208c8cb4a8c33f41b350315d63 --- /dev/null +++ b/projects/wobble_bobble/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/wobble_bobble/shaders/update_particle_velocities.comp b/projects/wobble_bobble/shaders/update_particle_velocities.comp new file mode 100644 index 0000000000000000000000000000000000000000..4420bc9575bcde4d04d7d4f5531d0091362285c2 --- /dev/null +++ b/projects/wobble_bobble/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/wobble_bobble/src/main.cpp b/projects/wobble_bobble/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1c3eb776b43d29ffe53aa62a03e3bc06fe145767 --- /dev/null +++ b/projects/wobble_bobble/src/main.cpp @@ -0,0 +1,838 @@ + +#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 = "Wobble Bobble"; + + 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, + 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::createVertexBinding(0, gfxProgramGrid.getVertexAttachments()) + }); + + vkcv::GraphicsPipelineConfig gfxPipelineConfigGrid ( + gfxProgramGrid, + gfxPassGrid, + vertexLayoutGrid, + { gfxSetLayoutGrid } + ); + + vkcv::VertexLayout vertexLayoutParticles ({ + vkcv::createVertexBinding(0, gfxProgramParticles.getVertexAttachments()) + }); + + vkcv::GraphicsPipelineConfig gfxPipelineConfigParticles ( + gfxProgramParticles, + gfxPassParticles, + vertexLayoutParticles, + { gfxSetLayoutParticles } + ); + + vkcv::VertexLayout vertexLayoutLines ({ + 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, + 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/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/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..567041bb602493196512b554014cf39277926bdc 100644 --- a/src/vkcv/BufferManager.cpp +++ b/src/vkcv/BufferManager.cpp @@ -3,139 +3,147 @@ * @file vkcv/BufferManager.cpp */ -#include "vkcv/BufferManager.hpp" +#include "BufferManager.hpp" #include "vkcv/Core.hpp" #include <vkcv/Logger.hpp> 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); + + m_stagingBuffer = createBuffer(TypeGuard(1), BufferType::STAGING, + BufferMemoryType::HOST_VISIBLE, 1024 * 1024, false); + + return true; } - - BufferManager::~BufferManager() noexcept { - for (uint64_t id = 0; id < m_buffers.size(); id++) { - destroyBufferById(id); - } + + uint64_t BufferManager::getIdFrom(const BufferHandle &handle) const { + return handle.getId(); } - - /** - * @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); + + 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) { + 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_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(); - + + if (readable) { + usageFlags |= vk::BufferUsageFlagBits::eTransferSrc; + } + + const vma::Allocator &allocator = getCore().getContext().getAllocator(); + vk::MemoryPropertyFlags memoryTypeFlags; + 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: + memoryTypeFlags = vk::MemoryPropertyFlagBits::eDeviceLocal; + memoryUsage = vma::MemoryUsage::eGpuOnly; + mappable = false; + break; + case BufferMemoryType::HOST_VISIBLE: + memoryTypeFlags = vk::MemoryPropertyFlagBits::eHostVisible + | vk::MemoryPropertyFlagBits::eHostCoherent; + memoryUsage = vma::MemoryUsage::eCpuOnly; + 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 - ); - - vk::DeviceMemory memory = device.allocateMemory(vk::MemoryAllocateInfo(requirements.size, memoryTypeIndex)); - - device.bindBufferMemory(buffer, memory, 0); - - 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 (type == BufferType::STAGING) { + memoryUsage = vma::MemoryUsage::eCpuToGpu; + } + + auto bufferAllocation = allocator.createBuffer( + vk::BufferCreateInfo(createFlags, size, usageFlags), + vma::AllocationCreateInfo(vma::AllocationCreateFlags(), memoryUsage, memoryTypeFlags, + memoryTypeFlags, 0, vma::Pool(), nullptr)); + + vk::Buffer buffer = bufferAllocation.first; + vma::Allocation allocation = bufferAllocation.second; + + return add({ typeGuard, type, memoryType, size, buffer, allocation, mappable }); } - - 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 +154,247 @@ 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; } - - if (id >= m_buffers.size()) { - return; - } - - auto& buffer = m_buffers[id]; - - if (buffer.m_mapped) { - return; - } - - const vk::Device& device = m_core->getContext().getDevice(); - + + 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); + void* mapped = allocator.mapMemory(buffer.m_allocation); + memcpy(reinterpret_cast<char*>(mapped) + offset, data, max_size); + allocator.unmapMemory(buffer.m_allocation); } else { - auto& stagingBuffer = m_buffers[ m_stagingBuffer.getId() ]; - - StagingStepInfo info; + auto &stagingBuffer = (*this) [m_stagingBuffer]; + + StagingWriteInfo 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); + + fillFromStagingBuffer(getCore(), info); } } - - void* BufferManager::mapBuffer(const BufferHandle& handle, size_t offset, size_t size) { - const uint64_t id = handle.getId(); - + + void BufferManager::readBuffer(const BufferHandle &handle, void* data, size_t size, + size_t offset) { + auto &buffer = (*this) [handle]; + if (size == 0) { size = SIZE_MAX; } - - if (id >= m_buffers.size()) { - return nullptr; - } - - auto& buffer = m_buffers[id]; - - if (buffer.m_mapped) { - return nullptr; - } - - const vk::Device& device = m_core->getContext().getDevice(); - + + const vma::Allocator &allocator = getCore().getContext().getAllocator(); + if (offset > buffer.m_size) { - return nullptr; - } - - 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; - } - - void BufferManager::unmapBuffer(const BufferHandle& handle) { - const uint64_t id = handle.getId(); - - if (id >= m_buffers.size()) { return; } - - auto& buffer = m_buffers[id]; - - if (buffer.m_mapped == nullptr) { - return; + + const size_t max_size = std::min(size, buffer.m_size - offset); + + if (buffer.m_mappable) { + 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 = (*this) [m_stagingBuffer]; + + StagingReadInfo 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; + + readToStagingBuffer(getCore(), info); } - - 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; - } - - 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; + + void* BufferManager::mapBuffer(const BufferHandle &handle, size_t offset, size_t size) { + auto &buffer = (*this) [handle]; + + if (size == 0) { + size = SIZE_MAX; } - - if (buffer.m_handle) { - device.destroyBuffer(buffer.m_handle); - buffer.m_handle = nullptr; + + const vma::Allocator &allocator = getCore().getContext().getAllocator(); + + if (offset > buffer.m_size) { + return nullptr; } + + return reinterpret_cast<char*>(allocator.mapMemory(buffer.m_allocation)) + offset; } - void BufferManager ::recordBufferMemoryBarrier(const BufferHandle& handle, vk::CommandBuffer cmdBuffer) { + void BufferManager::unmapBuffer(const BufferHandle &handle) { + auto &buffer = (*this) [handle]; - const uint64_t id = handle.getId(); + const vma::Allocator &allocator = getCore().getContext().getAllocator(); - if (id >= m_buffers.size()) { - vkcv_log(vkcv::LogLevel::ERROR, "Invalid buffer handle"); - return; - } + allocator.unmapMemory(buffer.m_allocation); + } + + void BufferManager ::recordBufferMemoryBarrier(const BufferHandle &handle, + vk::CommandBuffer cmdBuffer) { + auto &buffer = (*this) [handle]; + + vk::BufferMemoryBarrier memoryBarrier(vk::AccessFlagBits::eMemoryWrite, + vk::AccessFlagBits::eMemoryRead, 0, 0, + buffer.m_handle, 0, buffer.m_size); - 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..6220bb8ecba51fd1e1ae7b982b189cf0227bb6dc --- /dev/null +++ b/src/vkcv/BufferManager.hpp @@ -0,0 +1,183 @@ +#pragma once +/** + * @authors Tobias Frisch, Alexander Gauggel, Artur Wasmut, Lars Hoerttrich, Sebastian Gaida + * @file vkcv/BufferManager.hpp + * @brief Manager to handle buffer operations. + */ + +#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_mappable; + }; + + /** + * @brief Class to manage the creation, destruction, allocation + * and filling of buffers. + */ + class BufferManager : public HandleManager<BufferEntry, BufferHandle> { + friend class Core; + + private: + 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..a776042ffa1d0d5429b93dc02ae1d72a0a2e23ac 100644 --- a/src/vkcv/CommandStreamManager.cpp +++ b/src/vkcv/CommandStreamManager.cpp @@ -1,121 +1,105 @@ -#include "vkcv/CommandStreamManager.hpp" +#include "CommandStreamManager.hpp" #include "vkcv/Core.hpp" #include "vkcv/Logger.hpp" 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); + assert(device.waitForFences(waitFence, true, UINT64_MAX) == vk::Result::eSuccess); + + 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 e23213b41a3c9a289b679652b66bbe2e75cf0340..e642139fe71e988488cbd47041599a061b481d93 100644 --- a/src/vkcv/Context.cpp +++ b/src/vkcv/Context.cpp @@ -1,147 +1,135 @@ -#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; - } - - /** - * @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; +namespace vkcv { + 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,144 +143,342 @@ 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); + + std::vector<std::string> getRequiredExtensions() { + std::vector<std::string> extensions = Window::getExtensions(); #ifndef NDEBUG - extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + 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" - }; - + 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 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 + + if (featureManager.useExtension(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, false)) { + featureManager.useFeatures<vk::PhysicalDeviceShaderFloat16Int8Features>( + [](vk::PhysicalDeviceShaderFloat16Int8Features &features) { + features.setShaderFloat16(true); + }, + false + ); + } + + if (featureManager.useExtension(VK_KHR_16BIT_STORAGE_EXTENSION_NAME, false)) { + featureManager.useFeatures<vk::PhysicalDevice16BitStorageFeatures>( + [](vk::PhysicalDevice16BitStorageFeatures &features) { + features.setStorageBuffer16BitAccess(true); + }, + false + ); } + if (featureManager.useExtension(VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME, false)) { + featureManager.useFeatures<vk::PhysicalDeviceIndexTypeUint8FeaturesEXT>( + [](vk::PhysicalDeviceIndexTypeUint8FeaturesEXT &features) { + features.setIndexTypeUint8(true); + }, + false + ); + } + + featureManager.useFeatures([](vk::PhysicalDeviceFeatures &features) { + features.setFragmentStoresAndAtomics(true); + features.setGeometryShader(true); + features.setDepthClamp(true); + features.setShaderInt16(true); + }); + + 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 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; - deviceFeatures.depthClamp = 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); - - return Context(instance, physicalDevice, device, std::move(queueManager)); + + QueueManager queueManager = + QueueManager::create(device, queuePairsGraphics, queuePairsCompute, queuePairsTransfer); + + vma::AllocatorCreateFlags vmaFlags; + 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 352a1cf62eabe55ce1bbf2f53a6b5a4bd6e91753..6ccf1de841996c85905689f0ef45e3dbdcf7c285 100644 --- a/src/vkcv/Core.cpp +++ b/src/vkcv/Core.cpp @@ -1,594 +1,1292 @@ /** - * @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 -{ - - static std::vector<vk::ImageView> createSwapchainImageViews( Context &context, const std::vector<vk::Image>& images, - vk::Format format){ - 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, - format, - componentMapping, - subResourceRange); - - imageViews.push_back(context.getDevice().createImageView(imageViewCreateInfo)); - } - - return imageViews; - } - - 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 - ); - - Swapchain swapChain = Swapchain::create(window, context); - - const auto swapchainImages = context.getDevice().getSwapchainImagesKHR(swapChain.getSwapchain()); - const auto swapchainImageViews = createSwapchainImageViews( context, swapchainImages, swapChain.getFormat()); - - 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()); +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); + } + + 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; + } + + return m_ComputePipelineManager->createComputePipeline(config.getShaderProgram(), layouts); + } + + PassHandle Core::createPass(const PassConfig &config) { + return m_PassManager->createPass(config); + } + + 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); + } + + 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); + } - m_Context.m_Device.destroySwapchainKHR(m_swapchain.getSwapchain()); - m_Context.m_Instance.destroySurfaceKHR(m_swapchain.getSurface()); + void Core::readBuffer(const BufferHandle &handle, void* data, size_t size, size_t offset) { + m_BufferManager->readBuffer(handle, data, size, offset); } - PipelineHandle Core::createGraphicsPipeline(const PipelineConfig &config) - { - return m_PipelineManager->createPipeline(config, *m_PassManager); - } + void* Core::mapBuffer(const BufferHandle &handle, size_t offset, size_t size) { + return m_BufferManager->mapBuffer(handle, offset, size); + } - PipelineHandle Core::createComputePipeline( - const ShaderProgram &shaderProgram, - const std::vector<vk::DescriptorSetLayout>& descriptorSetLayouts) - { - return m_PipelineManager->createComputePipeline(shaderProgram, descriptorSetLayouts); - } + void Core::unmapBuffer(const BufferHandle &handle) { + m_BufferManager->unmapBuffer(handle); + } - PassHandle Core::createPass(const PassConfig &config) - { - return m_PassManager->createPass(config); - } + 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 (vk::DeviceLostError e) { + } catch (const vk::DeviceLostError &e) { result = vk::Result::eErrorDeviceLost; } - - if ((result != vk::Result::eSuccess) && - (result != vk::Result::eSuboptimalKHR)) { + + 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) { + } else if (result == vk::Result::eSuboptimalKHR) { vkcv_log(LogLevel::WARNING, "Acquired image is suboptimal"); - m_swapchain.signalSwapchainRecreation(); + 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); - - if (!m_swapchain.getSwapchain()) { + m_SwapchainManager->updateSwapchain(swapchainHandle, window); + + if (!m_SwapchainManager->getSwapchain(swapchainHandle).m_Swapchain) { return false; } - - const auto swapchainImages = m_Context.getDevice().getSwapchainImagesKHR(m_swapchain.getSwapchain()); - const auto swapchainViews = createSwapchainImageViews(m_Context, swapchainImages, m_swapchain.getFormat()); - - const auto& extent = m_swapchain.getExtent(); - - m_ImageManager->setSwapchainImages( - swapchainImages, - swapchainViews, - extent.width, extent.height, - m_swapchain.getFormat() - ); - } - - const auto& extent = m_swapchain.getExtent(); - + + setSwapchainImages(swapchainHandle); + } + + const auto &extent = m_SwapchainManager->getExtent(swapchainHandle); + width = extent.width; height = extent.height; - - if ((width < MIN_SWAPCHAIN_SIZE) || (height < MIN_SWAPCHAIN_SIZE)) { + + if ((width < MIN_SURFACE_SIZE) || (height < MIN_SURFACE_SIZE)) { return false; } - - if (acquireSwapchainImage() != Result::SUCCESS) { + + 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_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; } - // TODO: validate that width/height match for all attachments + } + + 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); + } + + 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 vk::RenderPass renderpass = m_PassManager->getVkPass(renderpassHandle); - const PassConfig passConfig = m_PassManager->getPassConfig(renderpassHandle); + const PassHandle &passHandle = pipelineManager.getPipelineConfig(pipelineHandle).getPass(); - const vk::Pipeline pipeline = m_PipelineManager->getVkPipeline(pipelineHandle); - const vk::PipelineLayout pipelineLayout = m_PipelineManager->getVkPipelineLayout(pipelineHandle); + const vk::RenderPass renderPass = passManager.getVkPass(passHandle); + const PassConfig passConfig = passManager.getPassConfig(passHandle); + + const auto &attachments = passConfig.getAttachments(); + const auto &layouts = passManager.getLayouts(passHandle); + + 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); + } + + 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); + } - recordCommandsToStream(cmdStreamHandle, submitFunction, finishFunction); + 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); + } - auto submitFunction = [&](const vk::CommandBuffer& cmdBuffer) { + static void recordMeshShaderDrawcall(const Core &core, + const DescriptorSetManager &descriptorSetManager, + vk::CommandBuffer cmdBuffer, + vk::PipelineLayout pipelineLayout, + const PushConstants &pushConstantData, + size_t drawcallIndex, const TaskDrawcall &drawcall) { - const auto pipelineLayout = m_PipelineManager->getVkPipelineLayout(computePipeline); + static PFN_vkCmdDrawMeshTasksNV cmdDrawMeshTasks = + reinterpret_cast<PFN_vkCmdDrawMeshTasksNV>( + core.getContext().getDevice().getProcAddr("vkCmdDrawMeshTasksNV")); - cmdBuffer.bindPipeline(vk::PipelineBindPoint::eCompute, m_PipelineManager->getVkPipeline(computePipeline)); - for (const auto& usage : descriptorSetUsages) { + 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)); + } + + 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; + } + + 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()); } - cmdBuffer.dispatch(dispatchCount[0], dispatchCount[1], dispatchCount[2]); + + 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(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 (vk::DeviceLostError e) { + } catch (const vk::DeviceLostError &e) { result = vk::Result::eErrorDeviceLost; } - - 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) { + + 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_swapchain.signalSwapchainRecreation(); - } - } - - 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(); + 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, - Multisampling multisampling) - { - + ImageHandle Core::createImage(vk::Format format, uint32_t width, uint32_t height, + uint32_t depth, bool createMipChain, bool supportStorage, + bool supportColorAttachment, Multisampling multisampling) { 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(width, std::max(height, depth)))); } - return Image::create( - m_ImageManager.get(), - format, - width, - height, - depth, - mipCount, - supportStorage, - supportColorAttachment, - multisampling); + return m_ImageManager->createImage(width, height, depth, format, mipCount, supportStorage, + supportColorAttachment, multisampling); } - const uint32_t Core::getImageWidth(ImageHandle imageHandle) - { - return m_ImageManager->getImageWidth(imageHandle); + void Core::fillImage(const ImageHandle &image, const void* data, size_t size) { + m_ImageManager->fillImage(image, data, size); } - const uint32_t Core::getImageHeight(ImageHandle imageHandle) - { - return m_ImageManager->getImageHeight(imageHandle); + void Core::switchImageLayout(const ImageHandle &image, vk::ImageLayout layout) { + m_ImageManager->switchImageLayoutImmediate(image, layout); } - DescriptorSetHandle Core::createDescriptorSet(const std::vector<DescriptorBinding>& bindings) - { - return m_DescriptorManager->createDescriptorSet(bindings); - } + Downsampler &Core::getDownsampler() { + return *m_downsampler; + } - void Core::writeDescriptorSet(DescriptorSetHandle handle, const DescriptorWrites &writes) { - m_DescriptorManager->writeDescriptorSet( - handle, - writes, - *m_ImageManager, - *m_BufferManager, - *m_SamplerManager); + 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); } - DescriptorSet Core::getDescriptorSet(const DescriptorSetHandle handle) const { - return m_DescriptorManager->getDescriptorSet(handle); + uint32_t Core::getImageDepth(const ImageHandle &image) { + return m_ImageManager->getImageDepth(image); } - void Core::prepareSwapchainImageForPresent(const CommandStreamHandle cmdStream) { + vk::Format Core::getImageFormat(const ImageHandle &image) { + return m_ImageManager->getImageFormat(image); + } + + bool Core::isImageSupportingStorage(const ImageHandle &image) { + return m_ImageManager->isImageSupportingStorage(image); + } + + 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_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) { - recordCommandsToStream(cmdStream, [image, this](const vk::CommandBuffer cmdBuffer) { - m_ImageManager->recordImageLayoutTransition(image, vk::ImageLayout::eShaderReadOnlyOptimal, 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) { - recordCommandsToStream(cmdStream, [image, this](const vk::CommandBuffer cmdBuffer) { - m_ImageManager->recordImageLayoutTransition(image, vk::ImageLayout::eGeneral, 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::recordImageMemoryBarrier(const CommandStreamHandle cmdStream, const ImageHandle image) { - recordCommandsToStream(cmdStream, [image, this](const vk::CommandBuffer cmdBuffer) { - m_ImageManager->recordImageMemoryBarrier(image, cmdBuffer); - }, nullptr); + void Core::prepareImageForAttachmentManually(const vk::CommandBuffer &cmdBuffer, + const ImageHandle &image) { + transitionRendertargetsToAttachmentLayout({ image }, *m_ImageManager, cmdBuffer); } - 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::updateImageLayoutManual(const vkcv::ImageHandle &image, + const vk::ImageLayout layout) { + m_ImageManager->updateImageLayoutManual(image, layout); } - - void Core::resolveMSAAImage(CommandStreamHandle cmdStream, ImageHandle src, ImageHandle dst) { - recordCommandsToStream(cmdStream, [src, dst, this](const vk::CommandBuffer cmdBuffer) { - m_ImageManager->recordMSAAResolve(cmdBuffer, src, dst); - }, 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::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..70fc7811791e02d57cb3a4de3d1c2f75db6e9b3a --- /dev/null +++ b/src/vkcv/DescriptorSetUsage.cpp @@ -0,0 +1,12 @@ + +#include "vkcv/DescriptorSetUsage.hpp" + +namespace vkcv { + + DescriptorSetUsage useDescriptorSet(uint32_t location, const DescriptorSetHandle &descriptorSet, + const std::vector<uint32_t> &dynamicOffsets) { + DescriptorSetUsage usage(location, descriptorSet, dynamicOffsets); + return usage; + } + +} // namespace vkcv diff --git a/src/vkcv/DescriptorWrites.cpp b/src/vkcv/DescriptorWrites.cpp new file mode 100644 index 0000000000000000000000000000000000000000..54db0de3c5c02082d775330d6931960e309f4ba8 --- /dev/null +++ b/src/vkcv/DescriptorWrites.cpp @@ -0,0 +1,76 @@ + +#include "vkcv/DescriptorWrites.hpp" + +namespace vkcv { + + DescriptorWrites &DescriptorWrites::writeSampledImage(uint32_t binding, ImageHandle image, + uint32_t mipLevel, bool useGeneralLayout, + uint32_t arrayIndex, uint32_t mipCount, + bool arrayView) { + m_sampledImageWrites.emplace_back(binding, image, mipLevel, useGeneralLayout, arrayIndex, + mipCount, arrayView); + + return *this; + } + + DescriptorWrites &DescriptorWrites::writeStorageImage(uint32_t binding, ImageHandle image, + uint32_t mipLevel, uint32_t mipCount, + bool arrayView) { + m_storageImageWrites.emplace_back(binding, image, mipLevel, mipCount, arrayView); + + return *this; + } + + DescriptorWrites &DescriptorWrites::writeUniformBuffer(uint32_t binding, BufferHandle buffer, + bool dynamic, uint32_t offset, + uint32_t size) { + m_uniformBufferWrites.emplace_back(binding, buffer, dynamic, offset, size); + return *this; + } + + DescriptorWrites &DescriptorWrites::writeStorageBuffer(uint32_t binding, BufferHandle buffer, + bool dynamic, uint32_t offset, + uint32_t size) { + m_storageBufferWrites.emplace_back(binding, buffer, dynamic, offset, size); + return *this; + } + + DescriptorWrites &DescriptorWrites::writeSampler(uint32_t binding, SamplerHandle sampler) { + m_samplerWrites.emplace_back(binding, sampler); + return *this; + } + + DescriptorWrites &DescriptorWrites::writeAcceleration( + uint32_t binding, const std::vector<vk::AccelerationStructureKHR> &structures) { + m_accelerationWrites.emplace_back(binding, structures); + 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..b34f81a2059033d0441af2e53612f3685d3c1d11 --- /dev/null +++ b/src/vkcv/Drawcall.cpp @@ -0,0 +1,54 @@ +/** + * @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) { + m_usages.emplace_back(location, descriptorSet, dynamicOffsets); + } + + 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 e6ea18588c251b5e49f454618a5ac9962cc8a264..0000000000000000000000000000000000000000 --- a/src/vkcv/DrawcallRecording.cpp +++ /dev/null @@ -1,47 +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; - - if (pushConstantData.data && pushConstantData.sizePerDrawcall > 0) { - 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, drawcall.instanceCount, 0, 0, {}); - } - else { - cmdBuffer.draw(drawcall.mesh.indexCount, 1, 0, 0, {}); - } - } -} diff --git a/src/vkcv/FeatureManager.cpp b/src/vkcv/FeatureManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bc9640c781c33abfb0ac7dee915696ce563bbc0a --- /dev/null +++ b/src/vkcv/FeatureManager.cpp @@ -0,0 +1,632 @@ + +#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::PhysicalDeviceIndexTypeUint8FeaturesEXT &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceIndexTypeUint8FeaturesEXT); + + vkcv_check_feature(indexTypeUint8); + + 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..255ce48e5b0fd3fa2b8a455699e18d2d0e9f756f --- /dev/null +++ b/src/vkcv/GraphicsPipelineManager.cpp @@ -0,0 +1,654 @@ +#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) { + // currently set to additive, if not disabled + // BlendFactors must be set as soon as additional BlendModes are added + static 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)); + + vk::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo( + {}, false, vk::LogicOp::eClear, + 1, // TODO: hardcoded to one + &colorBlendAttachmentState, { 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); + + // 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..abf5b9d7ec44699f6c581236b29fa921459c7ab4 100644 --- a/src/vkcv/Handles.cpp +++ b/src/vkcv/Handles.cpp @@ -1,99 +1,93 @@ #include "vkcv/Handles.hpp" +#include <iostream> + 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(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() { - 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; } - + 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; - + 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); } - + bool Handle::operator!() const { return (m_id == UINT64_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); } - + ImageHandle ImageHandle::createSwapchainImageHandle(const HandleDestroyFunction &destroy) { return ImageHandle(uint64_t(UINT64_MAX - 1), destroy); } - -} + +} // namespace vkcv diff --git a/src/vkcv/Image.cpp b/src/vkcv/Image.cpp index f8d94b734599cbf1f55aad7b590ab4796501d951..6e5b5fc1b3efbbcf4203e9825230eca20cbf55e5 100644 --- a/src/vkcv/Image.cpp +++ b/src/vkcv/Image.cpp @@ -4,81 +4,80 @@ * @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, - Multisampling msaa) - { - return Image( - manager, - manager->createImage(width, height, depth, format, mipCount, supportStorage, supportColorAttachment, msaa)); + 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); } - 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) { + return Image(&core, + core.createImage(format, width, height, depth, createMipChain, supportStorage, + supportColorAttachment, multisampling)); } - - Image::Image(ImageManager* manager, const ImageHandle& handle) : - m_manager(manager), - m_handle(handle) - {} -} +} // namespace vkcv diff --git a/src/vkcv/ImageConfig.cpp b/src/vkcv/ImageConfig.cpp deleted file mode 100644 index 80aeac3d0f702467c8edc1b0f7378d0d7f15b3db..0000000000000000000000000000000000000000 --- a/src/vkcv/ImageConfig.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include <vkcv/ImageConfig.hpp> -#include <vkcv/Logger.hpp> - -namespace vkcv { - vk::SampleCountFlagBits msaaToVkSampleCountFlag(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; - } - } -} \ 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 ae554e6babdd2b2f42c352515c02a34e45182fec..993243cb5644dd3a1527f3c50efadc9fdc9aa299 100644 --- a/src/vkcv/ImageManager.cpp +++ b/src/vkcv/ImageManager.cpp @@ -5,129 +5,196 @@ */ #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 (const auto swapchainImage : m_swapchainImages) { + + 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]; + } + + 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, - Multisampling msaa) - { - const vk::PhysicalDevice& physicalDevice = m_core->getContext().getPhysicalDevice(); - + ImageHandle ImageManager::createImage(uint32_t width, uint32_t height, uint32_t depth, + vk::Format format, uint32_t mipCount, bool supportStorage, + bool supportColorAttachment, Multisampling msaa) { + 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::ImageUsageFlags imageUsageFlags = + (vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst + | vk::ImageUsageFlagBits::eTransferSrc); + vk::ImageTiling imageTiling = vk::ImageTiling::eOptimal; - + if (supportStorage) { imageUsageFlags |= vk::ImageUsageFlagBits::eStorage; - - if (!(formatProperties.optimalTilingFeatures & vk::FormatFeatureFlagBits::eStorageImage)) { + + if (!(formatProperties.optimalTilingFeatures + & vk::FormatFeatureFlagBits::eStorageImage)) { imageTiling = vk::ImageTiling::eLinear; + + if (!(formatProperties.linearTilingFeatures + & vk::FormatFeatureFlagBits::eStorageImage)) + return {}; } } - + if (supportColorAttachment) { imageUsageFlags |= vk::ImageUsageFlagBits::eColorAttachment; } - + const bool isDepthFormat = isDepthImageFormat(format); - + if (isDepthFormat) { imageUsageFlags |= vk::ImageUsageFlagBits::eDepthStencilAttachment; } - const vk::Device& device = m_core->getContext().getDevice(); + const vma::Allocator &allocator = getCore().getContext().getAllocator(); vk::ImageType imageType = vk::ImageType::e3D; vk::ImageViewType imageViewType = vk::ImageViewType::e3D; - + if (depth <= 1) { if (height <= 1) { imageType = vk::ImageType::e1D; @@ -137,557 +204,382 @@ namespace vkcv { imageViewType = vk::ImageViewType::e2D; } } - + if (isDepthFormat) { imageType = vk::ImageType::e2D; imageViewType = vk::ImageViewType::e2D; } - + + 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 uint32_t arrayLayers = std::min<uint32_t>(1, imageFormatProperties.maxArrayLayers); - vk::SampleCountFlagBits sampleCountFlag = msaaToVkSampleCountFlag(msaa); + const vk::ImageFormatProperties imageFormatProperties = + physicalDevice.getImageFormatProperties(format, imageType, imageTiling, + imageUsageFlags); + + const uint32_t arrayLayers = std::min<uint32_t>(1, imageFormatProperties.maxArrayLayers); const vk::ImageCreateInfo imageCreateInfo( - createFlags, - imageType, - format, - vk::Extent3D(width, height, depth), - mipCount, - arrayLayers, - sampleCountFlag, - 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 - ); - - vk::DeviceMemory memory = device.allocateMemory(vk::MemoryAllocateInfo(requirements.size, memoryTypeIndex)); - device.bindImageMemory(image, memory, 0); + createFlags, imageType, format, vk::Extent3D(width, height, depth), mipCount, + arrayLayers, msaaToSampleCountFlagBits(msaa), imageTiling, imageUsageFlags, + vk::SharingMode::eExclusive, {}, vk::ImageLayout::eUndefined); + + auto imageAllocation = allocator.createImage( + imageCreateInfo, + vma::AllocationCreateInfo(vma::AllocationCreateFlags(), vma::MemoryUsage::eGpuOnly, + vk::MemoryPropertyFlagBits::eDeviceLocal, + vk::MemoryPropertyFlagBits::eDeviceLocal, 0, vma::Pool(), + nullptr)); + + vk::Image image = imageAllocation.first; + vma::Allocation allocation = imageAllocation.second; vk::ImageAspectFlags aspectFlags; - + if (isDepthFormat) { aspectFlags = vk::ImageAspectFlagBits::eDepth; } else { 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, + {}, 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 - ) - ); + 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); }); + + 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)); + } + + return add({ image, allocation, + + views, arrayViews, + + width, height, depth, + + format, arrayLayers, vk::ImageLayout::eUndefined, supportStorage }); } - - ImageHandle ImageManager::createSwapchainImage() { + + ImageHandle ImageManager::createSwapchainImage() const { 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; - } - - auto& image = m_images[id]; - + vk::Image ImageManager::getVulkanImage(const ImageHandle &handle) const { + auto &image = (*this) [handle]; return image.m_handle; } - - vk::DeviceMemory ImageManager::getVulkanDeviceMemory(const ImageHandle &handle) const { + 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]; - - return image.m_memory; - } - - vk::ImageView ImageManager::getVulkanImageView(const ImageHandle &handle, const size_t mipLevel) const { - + auto &image = (*this) [handle]; + const vma::Allocator &allocator = getCore().getContext().getAllocator(); + + auto info = allocator.getAllocationInfo(image.m_allocation); + + return info.deviceMemory; + } + + vk::ImageView ImageManager::getVulkanImageView(const ImageHandle &handle, size_t mipLevel, + bool arrayView) const { if (handle.isSwapchainImage()) { - return m_swapchainImages[m_currentSwapchainInputImage].m_viewPerMip[0]; + 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; - } - - const auto& image = m_images[id]; + const auto &image = (*this) [handle]; + const auto &views = arrayView ? image.m_arrayViewPerMip : image.m_viewPerMip; - if (mipLevel >= image.m_viewPerMip.size()) { + 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(); - - const bool isSwapchainImage = handle.isSwapchainImage(); - if (id >= m_images.size() && !isSwapchainImage) { - vkcv_log(LogLevel::ERROR, "Invalid handle"); - return; + 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; } - - auto& image = isSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[id]; - const auto transitionBarrier = createImageLayoutTransitionBarrier(image, newLayout); - - SubmitInfo submitInfo; - submitInfo.queueType = QueueType::Graphics; - - 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, - {}, - nullptr, - nullptr, - transitionBarrier - ); + + const uint32_t mipLevelsMax = image.m_viewPerMip.size(); + + if (mipLevelOffset > mipLevelsMax) + mipLevelOffset = mipLevelsMax; + + if ((!mipLevelCount) || (mipLevelOffset + mipLevelCount > mipLevelsMax)) + mipLevelCount = mipLevelsMax - mipLevelOffset; + + vk::ImageSubresourceRange imageSubresourceRange(aspectFlags, mipLevelOffset, mipLevelCount, + 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); + } + + 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); image.m_layout = newLayout; } - void ImageManager::recordImageLayoutTransition( - const ImageHandle& handle, - vk::ImageLayout newLayout, - vk::CommandBuffer cmdBuffer) { + 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); - const uint64_t id = handle.getId(); - const bool isSwapchainImage = handle.isSwapchainImage(); + cmdBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands, + vk::PipelineStageFlagBits::eAllCommands, {}, nullptr, nullptr, + transitionBarrier); - if (id >= m_images.size() && !isSwapchainImage) { - vkcv_log(LogLevel::ERROR, "Invalid handle"); - return; - } - - 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) { + void ImageManager::recordImageMemoryBarrier(const ImageHandle &handle, + vk::CommandBuffer cmdBuffer) { + auto &image = (*this) [handle]; + const auto transitionBarrier = + createImageLayoutTransitionBarrier(image, 0, 0, image.m_layout); - 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); + 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::eR8G8B8A8Srgb: - return 4; - case vk::Format::eR8G8B8A8Unorm: - return 4; - default: - std::cerr << "Unknown image format" << std::endl; - return 4; + 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 << "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) { if (handle.isSwapchainImage()) { vkcv_log(LogLevel::ERROR, "Swapchain image cannot be filled"); return; } - if (id >= m_images.size()) { - vkcv_log(LogLevel::ERROR, "Invalid handle"); - return; - } - - auto& image = m_images[id]; - - 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 - ); - - const size_t max_size = std::min(size, image_size); - - BufferHandle bufferHandle = m_bufferManager.createBuffer( - BufferType::STAGING, max_size, BufferMemoryType::HOST_VISIBLE - ); - - m_bufferManager.fillBuffer(bufferHandle, data, max_size, 0); - - vk::Buffer stagingBuffer = m_bufferManager.getBuffer(bufferHandle); - - SubmitInfo submitInfo; - submitInfo.queueType = QueueType::Transfer; - - m_core->recordAndSubmitCommandsImmediate( - submitInfo, - [&image, &stagingBuffer](const vk::CommandBuffer& commandBuffer) { - vk::ImageAspectFlags aspectFlags; - - if (isDepthImageFormat(image.m_format)) { - aspectFlags = vk::ImageAspectFlagBits::eDepth; - } else { - aspectFlags = vk::ImageAspectFlagBits::eColor; - } - - const vk::BufferImageCopy region ( - 0, - 0, - 0, - vk::ImageSubresourceLayers( - aspectFlags, - 0, - 0, - image.m_layers - ), - vk::Offset3D(0, 0, 0), - vk::Extent3D(image.m_width, image.m_height, image.m_depth) - ); - - commandBuffer.copyBufferToImage( - stagingBuffer, - image.m_handle, - vk::ImageLayout::eTransferDstOptimal, - 1, - ®ion - ); - }, - [&]() { - switchImageLayoutImmediate( - handle, - vk::ImageLayout::eShaderReadOnlyOptimal - ); - } - ); - } - - 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); - }; + auto &image = (*this) [handle]; + switchImageLayoutImmediate(handle, vk::ImageLayout::eTransferDstOptimal); - 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) }); + const size_t image_size = + (image.m_width * image.m_height * image.m_depth * getBytesPerPixel(image.m_format)); - cmdBuffer.blitImage( - image.m_handle, - vk::ImageLayout::eGeneral, - image.m_handle, - vk::ImageLayout::eGeneral, - region, - vk::Filter::eLinear); + const size_t max_size = std::min(size, image_size); - srcWidth = dstWidth; - srcHeight = dstHeight; - srcDepth = dstDepth; + BufferHandle bufferHandle = getBufferManager().createBuffer( + TypeGuard(1), BufferType::STAGING, BufferMemoryType::DEVICE_LOCAL, max_size, false); - dstWidth = half(dstWidth); - dstHeight = half(dstHeight); - dstDepth = half(dstDepth); + getBufferManager().fillBuffer(bufferHandle, data, max_size, 0); - recordImageMemoryBarrier(handle, cmdBuffer); - } - } + vk::Buffer stagingBuffer = getBufferManager().getBuffer(bufferHandle); - void ImageManager::generateImageMipChainImmediate(const ImageHandle& handle) { + auto &core = getCore(); + auto stream = core.createCommandStream(QueueType::Transfer); - const auto& device = m_core->getContext().getDevice(); + core.recordCommandsToStream( + stream, + [&image, &stagingBuffer](const vk::CommandBuffer &commandBuffer) { + vk::ImageAspectFlags aspectFlags; - SubmitInfo submitInfo; - submitInfo.queueType = QueueType::Graphics; + if (isDepthImageFormat(image.m_format)) { + aspectFlags = vk::ImageAspectFlagBits::eDepth; + } else { + aspectFlags = vk::ImageAspectFlagBits::eColor; + } - if (handle.isSwapchainImage()) { - vkcv_log(vkcv::LogLevel::ERROR, "You cannot generate a mip chain for the swapchain, what are you smoking?"); - return; - } + const vk::BufferImageCopy region( + 0, 0, 0, vk::ImageSubresourceLayers(aspectFlags, 0, 0, image.m_layers), + vk::Offset3D(0, 0, 0), + vk::Extent3D(image.m_width, image.m_height, image.m_depth)); - const auto record = [this, handle](const vk::CommandBuffer cmdBuffer) { - recordImageMipGenerationToCmdBuffer(cmdBuffer, handle); - }; + commandBuffer.copyBufferToImage(stagingBuffer, image.m_handle, + vk::ImageLayout::eTransferDstOptimal, 1, ®ion); + }, + [&]() { + switchImageLayoutImmediate(handle, vk::ImageLayout::eShaderReadOnlyOptimal); + }); - m_core->recordAndSubmitCommandsImmediate(submitInfo, record, nullptr); + core.submitCommandStream(stream, false); } 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); - } - void ImageManager::recordMSAAResolve(vk::CommandBuffer cmdBuffer, ImageHandle src, ImageHandle dst) { - - const uint64_t srcId = src.getId(); - const uint64_t dstId = dst.getId(); - - const bool isSrcSwapchainImage = src.isSwapchainImage(); - const bool isDstSwapchainImage = dst.isSwapchainImage(); - - const bool isSrcHandleInvalid = srcId >= m_images.size() && !isSrcSwapchainImage; - const bool isDstHandleInvalid = dstId >= m_images.size() && !isDstSwapchainImage; - - if (isSrcHandleInvalid || isDstHandleInvalid) { - vkcv_log(LogLevel::ERROR, "Invalid handle"); - return; - } + getCore().recordCommandsToStream(cmdStream, record, nullptr); + } - auto& srcImage = isSrcSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[srcId]; - auto& dstImage = isDstSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[dstId]; + void ImageManager::recordMSAAResolve(vk::CommandBuffer cmdBuffer, const ImageHandle &src, + const ImageHandle &dst) { + auto &srcImage = (*this) [src]; + auto &dstImage = (*this) [dst]; vk::ImageResolve region( vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1), vk::Offset3D(0, 0, 0), vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1), - vk::Offset3D(0, 0, 0), + vk::Offset3D(0, 0, 0), vk::Extent3D(dstImage.m_width, dstImage.m_height, dstImage.m_depth)); - recordImageLayoutTransition(src, vk::ImageLayout::eTransferSrcOptimal, cmdBuffer); - recordImageLayoutTransition(dst, vk::ImageLayout::eTransferDstOptimal, cmdBuffer); + recordImageLayoutTransition(src, 0, 0, vk::ImageLayout::eTransferSrcOptimal, cmdBuffer); + recordImageLayoutTransition(dst, 0, 0, vk::ImageLayout::eTransferDstOptimal, cmdBuffer); - cmdBuffer.resolveImage( - srcImage.m_handle, - srcImage.m_layout, - dstImage.m_handle, - dstImage.m_layout, - region); + cmdBuffer.resolveImage(srcImage.m_handle, srcImage.m_layout, dstImage.m_handle, + dstImage.m_layout, region); } 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; - } - - auto& image = isSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[id]; - + 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]; - + uint32_t ImageManager::getImageDepth(const ImageHandle &handle) const { + 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; - } + auto &image = (*this) [handle]; + return image.m_viewPerMip.size(); + } - return m_images[id].m_viewPerMip.size(); + uint32_t ImageManager::getImageArrayLayers(const ImageHandle &handle) const { + auto &image = (*this) [handle]; + return image.m_layers; } 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, + 1, + vk::ImageLayout::eUndefined, + false }); } } -} \ No newline at end of file + void ImageManager::updateImageLayoutManual(const vkcv::ImageHandle &handle, + const vk::ImageLayout layout) { + auto &image = (*this) [handle]; + image.m_layout = layout; + } + +} // namespace vkcv \ No newline at end of file diff --git a/src/vkcv/ImageManager.hpp b/src/vkcv/ImageManager.hpp index 1d8ce207b645e30cee291816eac3c934ed40e92a..ddc44f9b519061a7881eb0904034da2276b82a12 100644 --- a/src/vkcv/ImageManager.hpp +++ b/src/vkcv/ImageManager.hpp @@ -5,127 +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 "vkcv/ImageConfig.hpp" +#include "BufferManager.hpp" +#include "HandleManager.hpp" +#include "vkcv/Multisampling.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; + uint32_t m_layers; + vk::ImageLayout m_layout; + 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; + + [[nodiscard]] BufferManager &getBufferManager(); - void recordImageMipGenerationToCmdBuffer(vk::CommandBuffer cmdBuffer, const ImageHandle& handle); + 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, - Multisampling msaa); - - 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); - void recordMSAAResolve(vk::CommandBuffer cmdBuffer, ImageHandle src, 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]] - uint32_t getImageMipCount(const ImageHandle& handle) const; + ImageManager() noexcept; + + ~ImageManager() noexcept override; + + [[nodiscard]] ImageHandle createImage(uint32_t width, uint32_t height, uint32_t depth, + vk::Format format, uint32_t mipCount, + bool supportStorage, bool supportColorAttachment, + Multisampling msaa); + + [[nodiscard]] ImageHandle createSwapchainImage() const; + + [[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); + + 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, const 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 78bd5808b63fee7333243db4fca640047f76eae9..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, Multisampling msaa) noexcept : - attachments{std::move(attachments) }, msaa(msaa) - {} -} \ No newline at end of file +} // namespace vkcv diff --git a/src/vkcv/PassManager.cpp b/src/vkcv/PassManager.cpp index e50f800a482460cdb81687c76f8b1318598f689c..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, - msaaToVkSampleCountFlag(config.msaa), - 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 8b1f0b68be3a72f60103ca0dd8136f2c923513a5..0000000000000000000000000000000000000000 --- a/src/vkcv/PipelineManager.cpp +++ /dev/null @@ -1,460 +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; - } - } - - 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(vkcv::LogLevel::ERROR, "Unknown depth test enum"); return vk::CompareOp::eAlways; - } - } - - 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); - - vk::CullModeFlags cullMode; - switch (config.m_culling) { - case CullMode::None: cullMode = vk::CullModeFlagBits::eNone; break; - case CullMode::Front: cullMode = vk::CullModeFlagBits::eFront; break; - case CullMode::Back: cullMode = vk::CullModeFlagBits::eBack; break; - default: vkcv_log(vkcv::LogLevel::ERROR, "Unknown CullMode"); cullMode = vk::CullModeFlagBits::eNone; - } - - // rasterization state - vk::PipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo( - {}, - config.m_EnableDepthClamping, - false, - vk::PolygonMode::eFill, - cullMode, - 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( - {}, - msaaToVkSampleCountFlag(config.m_multisampling), - false, - 0.f, - nullptr, - config.m_alphaToCoverage, - 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); - - // currently set to additive, if not disabled - // BlendFactors must be set as soon as additional BlendModes are added - vk::PipelineColorBlendAttachmentState colorBlendAttachmentState( - config.m_blendMode != BlendMode::None, - 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 pushConstantSize = config.m_ShaderProgram.getPushConstantSize(); - const vk::PushConstantRange pushConstantRange(vk::ShaderStageFlagBits::eAll, 0, pushConstantSize); - - // pipeline layout - vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo( - {}, - (config.m_DescriptorLayouts), - (pushConstantRange)); - if (pushConstantSize == 0) { - pipelineLayoutCreateInfo.pushConstantRangeCount = 0; - } - - - 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(), - config.m_depthTest != DepthTest::None, - config.m_depthWrite, - depthTestToVkCompareOp(config.m_depthTest), - 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..6925d40c5aa967ca18cd0231499248a1006d52fe 100644 --- a/src/vkcv/ShaderProgram.cpp +++ b/src/vkcv/ShaderProgram.cpp @@ -8,217 +8,338 @@ #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.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; + + 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) { + 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; + } - m_VertexAttachments.emplace_back(attachment_loc, attachment_name, attachment_format); - } + 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) { + vkcv_log(LogLevel::WARNING, + "Attempting to overwrite already existing binding %u at set ID %u.", + bindingID, setID); + } } - //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 + 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) { + 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) { + 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) { + 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) { + 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 94e7301d66bfcc513434ef6d22520d1b95f98161..0000000000000000000000000000000000000000 --- a/src/vkcv/Swapchain.cpp +++ /dev/null @@ -1,273 +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."); - } - - 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) - }; - - 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); - - if ((extent2D.width >= MIN_SWAPCHAIN_SIZE) && (extent2D.height >= MIN_SWAPCHAIN_SIZE)) { - 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); - } else { - m_Swapchain = nullptr; - - signalSwapchainRecreation(); - } - - if (oldSwapchain) { - 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..9b9db90166a0be6f0e3a4a5666f837ceba293690 --- /dev/null +++ b/src/vkcv/VertexData.cpp @@ -0,0 +1,39 @@ + +#include "vkcv/VertexData.hpp" + +namespace vkcv { + + VertexBufferBinding vertexBufferBinding(const BufferHandle &buffer, size_t offset) { + VertexBufferBinding binding(buffer, 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..252973f539af51e67ecb30bbe86717961c3a17d6 100644 --- a/src/vkcv/VertexLayout.cpp +++ b/src/vkcv/VertexLayout.cpp @@ -6,57 +6,54 @@ #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 (uint32_t i = 0; i < attachments.size(); i++) { + bindings.push_back(createVertexBinding(i, { attachments [i] })); + } + + return bindings; + } + +} // namespace vkcv \ No newline at end of file diff --git a/src/vkcv/Window.cpp b/src/vkcv/Window.cpp index ea72582d67d5350e5fbf3f3c0fa2aae2ba407b0e..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,92 +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; - } - - void Window::getFramebufferSize(int &width, int &height) const { - glfwGetFramebufferSize(m_window, &width, &height); - } - -} + } + + 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