diff --git a/.github/workflows/build-nabla.yml b/.github/workflows/build-nabla.yml index 3e8e0b4dd0..b664527558 100644 --- a/.github/workflows/build-nabla.yml +++ b/.github/workflows/build-nabla.yml @@ -129,16 +129,31 @@ jobs: --preset ci-build-dynamic-${{ matrix.vendor }} ` -t run-compiler-explorer --config ${{ matrix.config }} - - name: Container – Install NSC + - name: Container – Build Examples + id: build-examples + continue-on-error: true + run: | + docker exec orphan ` + ${{ env.entry }} ${{ env.cmd }} -Command cmake --build ` + --preset ci-build-dynamic-${{ matrix.vendor }} ` + -t examples_tests\all --config ${{ matrix.config }} ` + -- -k 0 + + - name: Container – Install Nabla run: | docker exec orphan ` ${{ env.entry }} ${{ env.cmd }} -Command cmake --install ` ${{ env.binary }} --config ${{ matrix.config }} ` - --component Runtimes --prefix ${{ env.install }} + --prefix ${{ env.install }} + + - name: Container – Install Examples + id: install-examples + continue-on-error: true + run: | docker exec orphan ` ${{ env.entry }} ${{ env.cmd }} -Command cmake --install ` - ${{ env.binary }} --config ${{ matrix.config }} ` - --component Executables --prefix ${{ env.install }} + ${{ env.binary }}\examples_tests --config ${{ matrix.config }} ` + --prefix ${{ env.install }} - name: Container – Save NSC Image run: | diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 5bd2d6859f..b242904db1 100755 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -520,6 +520,8 @@ nbl_adjust_definitions() add_custom_target(3rdparty) add_dependencies(3rdparty ${NBL_3RDPARTY_TARGETS}) +NBL_ADJUST_FOLDERS(3rdaprty) + nbl_install_dir("${CMAKE_CURRENT_SOURCE_DIR}/parallel-hashmap/parallel_hashmap") # parent scope exports, must be at the end of the file diff --git a/3rdparty/Vulkan-Headers b/3rdparty/Vulkan-Headers index 234c4b7370..31aa7f634b 160000 --- a/3rdparty/Vulkan-Headers +++ b/3rdparty/Vulkan-Headers @@ -1 +1 @@ -Subproject commit 234c4b7370a8ea3239a214c9e871e4b17c89f4ab +Subproject commit 31aa7f634b052d87ede4664053e85f3f4d1d50d3 diff --git a/CMakeLists.txt b/CMakeLists.txt index c6664f8085..4e29839399 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -181,7 +181,7 @@ option(NBL_PCH "Enable pre-compiled header" ON) option(NBL_FAST_MATH "Enable fast low-precision math" ON) option(NBL_BUILD_EXAMPLES "Enable building examples" ON) option(NBL_BUILD_MITSUBA_LOADER "Enable nbl::ext::MitsubaLoader?" OFF) # TODO: once it compies turn this ON by default! -option(NBL_BUILD_IMGUI "Enable nbl::ext::ImGui?" OFF) +option(NBL_BUILD_IMGUI "Enable nbl::ext::ImGui?" ON) option(NBL_BUILD_OPTIX "Enable nbl::ext::OptiX?" OFF) if(NBL_COMPILE_WITH_CUDA) @@ -271,7 +271,7 @@ add_subdirectory(src/nbl) add_subdirectory("${NBL_PYTHON_MODULE_ROOT_PATH}" tests) # Python Framework if(NBL_BUILD_EXAMPLES) file(LOCK "${CMAKE_CURRENT_SOURCE_DIR}/examples_tests" DIRECTORY GUARD PROCESS RESULT_VARIABLE NBL_LOCK TIMEOUT 60) - add_subdirectory(examples_tests) + add_subdirectory(examples_tests EXCLUDE_FROM_ALL) file(LOCK "${CMAKE_CURRENT_SOURCE_DIR}/examples_tests" DIRECTORY RELEASE RESULT_VARIABLE NBL_LOCK) endif() add_subdirectory(tools) diff --git a/cmake/common.cmake b/cmake/common.cmake index 69a0a5b980..bb34e7979c 100755 --- a/cmake/common.cmake +++ b/cmake/common.cmake @@ -16,131 +16,50 @@ include_guard(GLOBAL) include(ProcessorCount) -function(nbl_handle_dll_definitions _TARGET_ _SCOPE_) - if(NOT TARGET Nabla) - message(FATAL_ERROR "Internal error, Nabla target must be defined!") - endif() - - if(NOT TARGET ${_TARGET_}) - message(FATAL_ERROR "Internal error, requsted \"${_TARGET_}\" is not defined!") - endif() - - if(NBL_COMPILER_DYNAMIC_RUNTIME) - set(_NABLA_OUTPUT_DIR_ "${NBL_ROOT_PATH_BINARY}/src/nbl/$/devshgraphicsprogramming.nabla") - - target_compile_definitions(${_TARGET_} ${_SCOPE_} - _NABLA_DLL_NAME_="$>";_NABLA_OUTPUT_DIR_="${_NABLA_OUTPUT_DIR_}" - ) - endif() - - target_compile_definitions(${_TARGET_} ${_SCOPE_} - _DXC_DLL_="${DXC_DLL}" - ) -endfunction() - -function(nbl_handle_runtime_lib_properties _TARGET_) - if(NOT TARGET ${_TARGET_}) - message(FATAL_ERROR "Internal error, requsted \"${_TARGET_}\" is not defined!") - endif() - - set_target_properties(${_TARGET_} PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>$<$:DLL>") -endfunction() - # Macro creating project for an executable # Project and target get its name from directory when this macro gets executed (truncating number in the beginning of the name and making all lower case) # Created because of common cmake code for examples and tools macro(nbl_create_executable_project _EXTRA_SOURCES _EXTRA_OPTIONS _EXTRA_INCLUDES _EXTRA_LIBS) get_filename_component(_NBL_PROJECT_DIRECTORY_ "${CMAKE_CURRENT_SOURCE_DIR}" ABSOLUTE) - include("scripts/nbl/projectTargetName") # sets EXECUTABLE_NAME - - if(MSVC) - set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT ${EXECUTABLE_NAME}) - endif() + get_filename_component(EXECUTABLE_NAME ${_NBL_PROJECT_DIRECTORY_} NAME) + string(REGEX REPLACE "^[0-9]+\." "" EXECUTABLE_NAME ${EXECUTABLE_NAME}) + string(TOLOWER ${EXECUTABLE_NAME} EXECUTABLE_NAME) + string(MAKE_C_IDENTIFIER ${EXECUTABLE_NAME} EXECUTABLE_NAME) project(${EXECUTABLE_NAME}) + set_directory_properties(PROPERTIES VS_STARTUP_PROJECT ${EXECUTABLE_NAME}) + + set(NBL_EXECUTABLE_SOURCES + main.cpp + ${_EXTRA_SOURCES} + ) if(ANDROID) - add_library(${EXECUTABLE_NAME} SHARED main.cpp ${_EXTRA_SOURCES}) + add_library(${EXECUTABLE_NAME} SHARED ${NBL_EXECUTABLE_SOURCES}) else() - set(NBL_EXECUTABLE_SOURCES - main.cpp - ${_EXTRA_SOURCES} - ) - add_executable(${EXECUTABLE_NAME} ${NBL_EXECUTABLE_SOURCES}) - nbl_handle_runtime_lib_properties(${EXECUTABLE_NAME}) endif() - - nbl_handle_dll_definitions(${EXECUTABLE_NAME} PUBLIC) target_compile_definitions(${EXECUTABLE_NAME} PUBLIC _NBL_APP_NAME_="${EXECUTABLE_NAME}") - - if("${EXECUTABLE_NAME}" STREQUAL commonpch) - add_dependencies(${EXECUTABLE_NAME} Nabla) - else() - string(FIND "${_NBL_PROJECT_DIRECTORY_}" "${NBL_ROOT_PATH}/examples_tests" _NBL_FOUND_) - - if(NOT "${_NBL_FOUND_}" STREQUAL "-1") # the call was made for a target defined in examples_tests, request common api PCH - if(NOT TARGET ${NBL_EXECUTABLE_COMMON_API_TARGET}) - message(FATAL_ERROR "Internal error, NBL_EXECUTABLE_COMMON_API_TARGET target must be defined to create an example target!") - endif() - - add_dependencies(${EXECUTABLE_NAME} ${NBL_EXECUTABLE_COMMON_API_TARGET}) - target_link_libraries(${EXECUTABLE_NAME} PUBLIC ${NBL_EXECUTABLE_COMMON_API_TARGET}) - target_precompile_headers("${EXECUTABLE_NAME}" REUSE_FROM "${NBL_EXECUTABLE_COMMON_API_TARGET}") - endif() - endif() target_include_directories(${EXECUTABLE_NAME} PUBLIC "${NBL_ROOT_PATH}/examples_tests/common" - PUBLIC "${NBL_ROOT_PATH_BINARY}/include" - PUBLIC ../../include # in macro.. relative to what? TODO: correct PRIVATE ${_EXTRA_INCLUDES} ) target_link_libraries(${EXECUTABLE_NAME} PUBLIC Nabla ${_EXTRA_LIBS}) - add_compile_options(${_EXTRA_OPTIONS}) - - if(NBL_SANITIZE_ADDRESS) - if(MSVC) - target_compile_options(${EXECUTABLE_NAME} PUBLIC /fsanitize=address) - else() - target_compile_options(${EXECUTABLE_NAME} PUBLIC -fsanitize=address) - endif() - endif() - - if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - # add_compile_options("-msse4.2 -mfpmath=sse") ???? - add_compile_options( - "$<$:-fstack-protector-all>" - ) - - set(COMMON_LINKER_OPTIONS "-msse4.2 -mfpmath=sse -fuse-ld=gold") - set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${COMMON_LINKER_OPTIONS}") - set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${COMMON_LINKER_OPTIONS} -fstack-protector-strong") - if (NBL_GCC_SANITIZE_ADDRESS) - set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} -fsanitize=address") - endif() - if (NBL_GCC_SANITIZE_THREAD) - set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} -fsanitize=thread") - endif() - if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 6.1) - add_compile_options(-Wno-error=ignored-attributes) - endif() - endif() - nbl_adjust_flags(TARGET ${EXECUTABLE_NAME} MAP_RELEASE Release MAP_RELWITHDEBINFO RelWithDebInfo MAP_DEBUG Debug) - nbl_adjust_definitions() # macro defined in root CMakeLists - add_definitions(-D_NBL_PCH_IGNORE_PRIVATE_HEADERS) + nbl_adjust_definitions() - set_target_properties(${EXECUTABLE_NAME} PROPERTIES DEBUG_POSTFIX _d) - set_target_properties(${EXECUTABLE_NAME} PROPERTIES RELWITHDEBINFO_POSTFIX _rwdi) - set_target_properties(${EXECUTABLE_NAME} - PROPERTIES + add_compile_options(${_EXTRA_OPTIONS}) + add_definitions(-D_NBL_PCH_IGNORE_PRIVATE_HEADERS) # TODO: wipe when we finally make Nabla PCH work as its supposed to + set_target_properties(${EXECUTABLE_NAME} PROPERTIES + DEBUG_POSTFIX _d + RELWITHDEBINFO_POSTFIX _rwdi RUNTIME_OUTPUT_DIRECTORY_DEBUG "${PROJECT_SOURCE_DIR}/bin" RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${PROJECT_SOURCE_DIR}/bin" RUNTIME_OUTPUT_DIRECTORY_RELEASE "${PROJECT_SOURCE_DIR}/bin" - VS_DEBUGGER_WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" # for visual studio + VS_DEBUGGER_WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" ) if(MSVC) # nothing special @@ -248,66 +167,28 @@ macro(nbl_create_executable_project _EXTRA_SOURCES _EXTRA_OPTIONS _EXTRA_INCLUDE nbl_project_process_test_module() endmacro() -# TODO this macro needs more love macro(nbl_create_ext_library_project EXT_NAME LIB_HEADERS LIB_SOURCES LIB_INCLUDES LIB_OPTIONS DEF_OPTIONS) set(LIB_NAME "NblExt${EXT_NAME}") project(${LIB_NAME}) add_library(${LIB_NAME} ${LIB_SOURCES}) - - target_include_directories(${LIB_NAME} - PUBLIC $ - PRIVATE ${LIB_INCLUDES} - ) - - if(NBL_EMBED_BUILTIN_RESOURCES) - get_target_property(_BUILTIN_RESOURCES_INCLUDE_SEARCH_DIRECTORY_ nblBuiltinResourceData BUILTIN_RESOURCES_INCLUDE_SEARCH_DIRECTORY) - - target_include_directories(${LIB_NAME} - PUBLIC ${_BUILTIN_RESOURCES_INCLUDE_SEARCH_DIRECTORY_} - ) - endif() - add_dependencies(${LIB_NAME} Nabla) target_link_libraries(${LIB_NAME} PUBLIC Nabla) - target_compile_options(${LIB_NAME} PUBLIC ${LIB_OPTIONS}) - target_compile_definitions(${LIB_NAME} PUBLIC ${DEF_OPTIONS}) - - nbl_handle_dll_definitions(${LIB_NAME} PUBLIC) - nbl_handle_runtime_lib_properties(${LIB_NAME}) - - if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - add_compile_options( - "$<$:-fstack-protector-all>" - ) - - set(COMMON_LINKER_OPTIONS "-msse4.2 -mfpmath=sse -fuse-ld=gold") - set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${COMMON_LINKER_OPTIONS}") - set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${COMMON_LINKER_OPTIONS} -fstack-protector-strong -fsanitize=address") - endif() + target_include_directories(${LIB_NAME} PRIVATE ${LIB_INCLUDES}) nbl_adjust_flags(TARGET ${LIB_NAME} MAP_RELEASE Release MAP_RELWITHDEBINFO RelWithDebInfo MAP_DEBUG Debug) - nbl_adjust_definitions() # macro defined in root CMakeLists + nbl_adjust_definitions() - set_target_properties(${LIB_NAME} PROPERTIES DEBUG_POSTFIX "") - set_target_properties(${LIB_NAME} PROPERTIES RELWITHDEBINFO_POSTFIX _rwdb) - set_target_properties(${LIB_NAME} - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" + target_compile_options(${LIB_NAME} PUBLIC ${LIB_OPTIONS}) + target_compile_definitions(${LIB_NAME} PUBLIC ${DEF_OPTIONS}) + set_target_properties(${LIB_NAME} PROPERTIES + DEBUG_POSTFIX _d + RELWITHDEBINFO_POSTFIX _rwdi ) - if(MSVC) - set_target_properties(${LIB_NAME} - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY_DEBUG "${PROJECT_SOURCE_DIR}/bin" - RUNTIME_OUTPUT_DIRECTORY_RELEASE "${PROJECT_SOURCE_DIR}/bin" - RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${PROJECT_SOURCE_DIR}/bin" - VS_DEBUGGER_WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" # seems like has no effect - ) - endif() if(LIB_HEADERS) nbl_install_file_spec(${LIB_HEADERS} "nbl/ext/${EXT_NAME}") - endif() + endif() nbl_install_lib_spec(${LIB_NAME} "nbl/ext/${EXT_NAME}") @@ -1334,4 +1215,17 @@ macro(NBL_DOCKER) RESULT_VARIABLE DOCKER_EXIT_CODE OUTPUT_VARIABLE DOCKER_OUTPUT_VAR ) -endmacro() \ No newline at end of file +endmacro() + +function(NBL_ADJUST_FOLDERS NS) + NBL_GET_ALL_TARGETS(TARGETS) + foreach(T IN LISTS TARGETS) + get_target_property(NBL_FOLDER ${T} FOLDER) + + if(NBL_FOLDER) + set_target_properties(${T} PROPERTIES FOLDER "nabla/${NS}/${NBL_FOLDER}") + else() + set_target_properties(${T} PROPERTIES FOLDER "nabla/${NS}") + endif() + endforeach() +endfunction() \ No newline at end of file diff --git a/examples_tests b/examples_tests index 643b8d8c40..a6de5908a2 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 643b8d8c400c7f9638dba99937af2f0d428c8849 +Subproject commit a6de5908a269d0f6853e0c1e94dec8fcdbe6540e diff --git a/include/nbl/application_templates/BasicMultiQueueApplication.hpp b/include/nbl/application_templates/BasicMultiQueueApplication.hpp index b4d9f1b843..f39e35ed07 100644 --- a/include/nbl/application_templates/BasicMultiQueueApplication.hpp +++ b/include/nbl/application_templates/BasicMultiQueueApplication.hpp @@ -1,11 +1,13 @@ // Copyright (C) 2023-2023 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h -#ifndef _NBL_EXAMPLES_APPLICATION_TEMPLATES_BASIC_MULTI_QUEUE_APPLICATION_HPP_INCLUDED_ -#define _NBL_EXAMPLES_APPLICATION_TEMPLATES_BASIC_MULTI_QUEUE_APPLICATION_HPP_INCLUDED_ +#ifndef _NBL_APPLICATION_TEMPLATES_BASIC_MULTI_QUEUE_APPLICATION_HPP_INCLUDED_ +#define _NBL_APPLICATION_TEMPLATES_BASIC_MULTI_QUEUE_APPLICATION_HPP_INCLUDED_ + // Build on top of the previous one -#include "MonoDeviceApplication.hpp" +#include "nbl/application_templates/MonoDeviceApplication.hpp" + namespace nbl::application_templates { @@ -263,5 +265,4 @@ class BasicMultiQueueApplication : public virtual MonoDeviceApplication }; } - -#endif // _CAMERA_IMPL_ \ No newline at end of file +#endif \ No newline at end of file diff --git a/include/nbl/application_templates/MonoAssetManagerAndBuiltinResourceApplication.hpp b/include/nbl/application_templates/MonoAssetManagerAndBuiltinResourceApplication.hpp deleted file mode 100644 index 1d3b81098c..0000000000 --- a/include/nbl/application_templates/MonoAssetManagerAndBuiltinResourceApplication.hpp +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (C) 2023-2023 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h -#ifndef _NBL_EXAMPLES_APPLICATION_TEMPLATES_MONO_ASSET_MANAGER_AND_BUILTIN_RESOURCE_APPLICATION_HPP_INCLUDED_ -#define _NBL_EXAMPLES_APPLICATION_TEMPLATES_MONO_ASSET_MANAGER_AND_BUILTIN_RESOURCE_APPLICATION_HPP_INCLUDED_ - -// we need a system and a logger -#include "MonoSystemMonoLoggerApplication.hpp" -#ifdef NBL_EMBED_BUILTIN_RESOURCES -#include "nbl/this_example/builtin/CArchive.h" -#endif - -namespace nbl::application_templates -{ - -// Virtual Inheritance because apps might end up doing diamond inheritance -class MonoAssetManagerAndBuiltinResourceApplication : public virtual MonoSystemMonoLoggerApplication -{ - using base_t = MonoSystemMonoLoggerApplication; - - public: - using base_t::base_t; - - protected: - // need this one for skipping passing all args into ApplicationFramework - MonoAssetManagerAndBuiltinResourceApplication() = default; - - virtual bool onAppInitialized(core::smart_refctd_ptr&& system) override - { - if (!base_t::onAppInitialized(std::move(system))) - return false; - - using namespace core; - m_assetMgr = make_smart_refctd_ptr(smart_refctd_ptr(m_system)); - - auto resourceArchive = - #ifdef NBL_EMBED_BUILTIN_RESOURCES - make_smart_refctd_ptr(smart_refctd_ptr(m_logger)); - #else - make_smart_refctd_ptr(localInputCWD/"app_resources",smart_refctd_ptr(m_logger),m_system.get()); - #endif - m_system->mount(std::move(resourceArchive),"app_resources"); - - return true; - } - - core::smart_refctd_ptr m_assetMgr; -}; - -} - -#endif // _CAMERA_IMPL_ \ No newline at end of file diff --git a/include/nbl/application_templates/MonoAssetManagerApplication.hpp b/include/nbl/application_templates/MonoAssetManagerApplication.hpp new file mode 100644 index 0000000000..975bc8db47 --- /dev/null +++ b/include/nbl/application_templates/MonoAssetManagerApplication.hpp @@ -0,0 +1,42 @@ +// Copyright (C) 2023-2023 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_APPLICATION_TEMPLATES_MONO_ASSET_MANAGER_APPLICATION_HPP_INCLUDED_ +#define _NBL_APPLICATION_TEMPLATES_MONO_ASSET_MANAGER_APPLICATION_HPP_INCLUDED_ + + +// we need a system and a logger +#include "nbl/application_templates/MonoSystemMonoLoggerApplication.hpp" + + +namespace nbl::application_templates +{ + +// Virtual Inheritance because apps might end up doing diamond inheritance +class MonoAssetManagerApplication : public virtual MonoSystemMonoLoggerApplication +{ + using base_t = MonoSystemMonoLoggerApplication; + + public: + using base_t::base_t; + + protected: + // need this one for skipping passing all args into ApplicationFramework + MonoAssetManagerApplication() = default; + + virtual bool onAppInitialized(core::smart_refctd_ptr&& system) override + { + if (!base_t::onAppInitialized(std::move(system))) + return false; + + using namespace core; + m_assetMgr = make_smart_refctd_ptr(smart_refctd_ptr(m_system)); + + return true; + } + + core::smart_refctd_ptr m_assetMgr; +}; + +} +#endif \ No newline at end of file diff --git a/include/nbl/application_templates/MonoDeviceApplication.hpp b/include/nbl/application_templates/MonoDeviceApplication.hpp index 8006ff36a5..a3a169d7b7 100644 --- a/include/nbl/application_templates/MonoDeviceApplication.hpp +++ b/include/nbl/application_templates/MonoDeviceApplication.hpp @@ -1,11 +1,13 @@ // Copyright (C) 2023-2023 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h -#ifndef _NBL_EXAMPLES_APPLICATION_TEMPLATES_MONO_DEVICE_APPLICATION_HPP_INCLUDED_ -#define _NBL_EXAMPLES_APPLICATION_TEMPLATES_MONO_DEVICE_APPLICATION_HPP_INCLUDED_ +#ifndef _NBL_APPLICATION_TEMPLATES_MONO_DEVICE_APPLICATION_HPP_INCLUDED_ +#define _NBL_APPLICATION_TEMPLATES_MONO_DEVICE_APPLICATION_HPP_INCLUDED_ + // Build on top of the previous one -#include "MonoSystemMonoLoggerApplication.hpp" +#include "nbl/application_templates/MonoSystemMonoLoggerApplication.hpp" + namespace nbl::application_templates { @@ -41,7 +43,8 @@ class MonoDeviceApplication : public virtual MonoSystemMonoLoggerApplication using namespace nbl::core; using namespace nbl::video; // TODO: specify version of the app - m_api = CVulkanConnection::create(smart_refctd_ptr(m_system),0,_NBL_APP_NAME_,smart_refctd_ptr(base_t::m_logger),getAPIFeaturesToEnable()); + // TODO: take APP NAME from executable metadata, DO NOT use defines in order to allow this to be part of examples PCH + m_api = CVulkanConnection::create(smart_refctd_ptr(m_system),0,"Nabla Example", smart_refctd_ptr(base_t::m_logger), getAPIFeaturesToEnable()); if (!m_api) return logFail("Failed to crate an IAPIConnection!"); @@ -276,5 +279,4 @@ class MonoDeviceApplication : public virtual MonoSystemMonoLoggerApplication }; } - -#endif // _CAMERA_IMPL_ \ No newline at end of file +#endif \ No newline at end of file diff --git a/include/nbl/application_templates/MonoSystemMonoLoggerApplication.hpp b/include/nbl/application_templates/MonoSystemMonoLoggerApplication.hpp index acc3f7a3ed..e995381a40 100644 --- a/include/nbl/application_templates/MonoSystemMonoLoggerApplication.hpp +++ b/include/nbl/application_templates/MonoSystemMonoLoggerApplication.hpp @@ -1,8 +1,8 @@ // Copyright (C) 2023-2023 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h -#ifndef _NBL_EXAMPLES_APPLICATION_TEMPLATES_MONO_SYSTEM_MONO_LOGGER_APPLICATION_HPP_INCLUDED_ -#define _NBL_EXAMPLES_APPLICATION_TEMPLATES_MONO_SYSTEM_MONO_LOGGER_APPLICATION_HPP_INCLUDED_ +#ifndef _NBL_APPLICATION_TEMPLATES_MONO_SYSTEM_MONO_LOGGER_APPLICATION_HPP_INCLUDED_ +#define _NBL_APPLICATION_TEMPLATES_MONO_SYSTEM_MONO_LOGGER_APPLICATION_HPP_INCLUDED_ // always include nabla first #include "nabla.h" diff --git a/include/nbl/asset/ECommonEnums.h b/include/nbl/asset/ECommonEnums.h index 7db562cc6a..c07a0ced6a 100644 --- a/include/nbl/asset/ECommonEnums.h +++ b/include/nbl/asset/ECommonEnums.h @@ -185,6 +185,123 @@ struct SMemoryBarrier } }; +inline core::bitflag allPreviousStages(core::bitflag stages) +{ + struct PerStagePreviousStages + { + public: + constexpr PerStagePreviousStages() + { + // set all stage to have itself as their previous stages + for (auto i = 0; i < std::numeric_limits::digits; i++) + data[i] = static_cast(i); + + add(PIPELINE_STAGE_FLAGS::COMPUTE_SHADER_BIT, PIPELINE_STAGE_FLAGS::DISPATCH_INDIRECT_COMMAND_BIT); + + add(PIPELINE_STAGE_FLAGS::RAY_TRACING_SHADER_BIT, PIPELINE_STAGE_FLAGS::DISPATCH_INDIRECT_COMMAND_BIT); + + // graphics primitive pipeline + PIPELINE_STAGE_FLAGS primitivePrevStage = PIPELINE_STAGE_FLAGS::DISPATCH_INDIRECT_COMMAND_BIT; + for (auto pipelineStage : {PIPELINE_STAGE_FLAGS::INDEX_INPUT_BIT, PIPELINE_STAGE_FLAGS::VERTEX_ATTRIBUTE_INPUT_BIT, PIPELINE_STAGE_FLAGS::VERTEX_SHADER_BIT, PIPELINE_STAGE_FLAGS::TESSELLATION_CONTROL_SHADER_BIT, PIPELINE_STAGE_FLAGS::TESSELLATION_EVALUATION_SHADER_BIT, PIPELINE_STAGE_FLAGS::GEOMETRY_SHADER_BIT, PIPELINE_STAGE_FLAGS::SHADING_RATE_ATTACHMENT_BIT, PIPELINE_STAGE_FLAGS::EARLY_FRAGMENT_TESTS_BIT, PIPELINE_STAGE_FLAGS::FRAGMENT_SHADER_BIT, PIPELINE_STAGE_FLAGS::LATE_FRAGMENT_TESTS_BIT, PIPELINE_STAGE_FLAGS::COLOR_ATTACHMENT_OUTPUT_BIT}) + { + if (pipelineStage == PIPELINE_STAGE_FLAGS::EARLY_FRAGMENT_TESTS_BIT) + primitivePrevStage |= PIPELINE_STAGE_FLAGS::FRAGMENT_DENSITY_PROCESS_BIT; + add(pipelineStage, primitivePrevStage); + primitivePrevStage |= pipelineStage; + } + + + } + constexpr const auto& operator[](const size_t ix) const {return data[ix];} + + private: + constexpr static uint8_t findLSB(size_t val) + { + for (size_t ix=0ull; ix(stageFlag)); + data[bitIx] |= previousStageFlags; + } + + PIPELINE_STAGE_FLAGS data[std::numeric_limits>::digits] = {}; + }; + + constexpr PerStagePreviousStages bitToAccess = {}; + + core::bitflag retval = PIPELINE_STAGE_FLAGS::NONE; + while (bool(stages.value)) + { + const auto bitIx = hlsl::findLSB(stages); + retval |= bitToAccess[bitIx]; + stages ^= static_cast(0x1u< allLaterStages(core::bitflag stages) +{ + struct PerStageLaterStages + { + public: + constexpr PerStageLaterStages() + { + // set all stage to have itself as their next stages + for (auto i = 0; i < std::numeric_limits::digits; i++) + data[i] = static_cast(i); + + add(PIPELINE_STAGE_FLAGS::DISPATCH_INDIRECT_COMMAND_BIT, PIPELINE_STAGE_FLAGS::COMPUTE_SHADER_BIT); + add(PIPELINE_STAGE_FLAGS::DISPATCH_INDIRECT_COMMAND_BIT, PIPELINE_STAGE_FLAGS::RAY_TRACING_SHADER_BIT); + + // graphics primitive pipeline + PIPELINE_STAGE_FLAGS laterStage = PIPELINE_STAGE_FLAGS::NONE; + const auto graphicsPrimitivePipelineOrders = std::array{ PIPELINE_STAGE_FLAGS::DISPATCH_INDIRECT_COMMAND_BIT, PIPELINE_STAGE_FLAGS::INDEX_INPUT_BIT, PIPELINE_STAGE_FLAGS::VERTEX_ATTRIBUTE_INPUT_BIT, PIPELINE_STAGE_FLAGS::VERTEX_SHADER_BIT, PIPELINE_STAGE_FLAGS::TESSELLATION_CONTROL_SHADER_BIT, PIPELINE_STAGE_FLAGS::TESSELLATION_EVALUATION_SHADER_BIT, PIPELINE_STAGE_FLAGS::GEOMETRY_SHADER_BIT, PIPELINE_STAGE_FLAGS::SHADING_RATE_ATTACHMENT_BIT, PIPELINE_STAGE_FLAGS::EARLY_FRAGMENT_TESTS_BIT, PIPELINE_STAGE_FLAGS::FRAGMENT_SHADER_BIT, PIPELINE_STAGE_FLAGS::LATE_FRAGMENT_TESTS_BIT, PIPELINE_STAGE_FLAGS::COLOR_ATTACHMENT_OUTPUT_BIT }; + for (auto iter = graphicsPrimitivePipelineOrders.rbegin(); iter < graphicsPrimitivePipelineOrders.rend(); iter++) + { + const auto pipelineStage = *iter; + add(pipelineStage, laterStage); + laterStage |= pipelineStage; + } + + add(PIPELINE_STAGE_FLAGS::FRAGMENT_DENSITY_PROCESS_BIT, PIPELINE_STAGE_FLAGS::EARLY_FRAGMENT_TESTS_BIT); + } + constexpr const auto& operator[](const size_t ix) const {return data[ix];} + + private: + constexpr static uint8_t findLSB(size_t val) + { + for (size_t ix=0ull; ix(stageFlag)); + data[bitIx] |= laterStageFlags; + } + + PIPELINE_STAGE_FLAGS data[std::numeric_limits>::digits] = {}; + }; + + constexpr PerStageLaterStages bitToAccess = {}; + + core::bitflag retval = PIPELINE_STAGE_FLAGS::NONE; + while (bool(stages.value)) + { + const auto bitIx = hlsl::findLSB(stages); + retval |= bitToAccess[bitIx]; + stages ^= static_cast(0x1u< allAccessesFromStages(core::bitflag stages) { struct PerStageAccesses @@ -257,6 +374,9 @@ inline core::bitflag allAccessesFromStages(core::bitflag retval = ACCESS_FLAGS::NONE; while (bool(stages.value)) { diff --git a/include/nbl/asset/IAsset.h b/include/nbl/asset/IAsset.h index aae73fac2a..a691fa6af6 100644 --- a/include/nbl/asset/IAsset.h +++ b/include/nbl/asset/IAsset.h @@ -83,14 +83,14 @@ class IAsset : virtual public core::IReferenceCounted ET_ANIMATION_LIBRARY = 1ull<<8, //!< asset::ICPUAnimationLibrary ET_PIPELINE_LAYOUT = 1ull<<9, //!< asset::ICPUPipelineLayout ET_SHADER = 1ull<<10, //!< asset::IShader - ET_RENDERPASS_INDEPENDENT_PIPELINE = 1ull<<12, //!< asset::ICPURenderpassIndependentPipeline + ET_GEOMETRY = 1ull<<12, //!< anything inheriting from asset::IGeometry ET_RENDERPASS = 1ull<<13, //!< asset::ICPURenderpass ET_FRAMEBUFFER = 1ull<<14, //!< asset::ICPUFramebuffer ET_GRAPHICS_PIPELINE = 1ull<<15, //!< asset::ICPUGraphicsPipeline ET_BOTOM_LEVEL_ACCELERATION_STRUCTURE = 1ull<<16, //!< asset::ICPUBottomLevelAccelerationStructure ET_TOP_LEVEL_ACCELERATION_STRUCTURE = 1ull<<17, //!< asset::ICPUTopLevelAccelerationStructure - ET_SUB_MESH = 1ull<<18, //!< DEPRECATED asset::ICPUMeshBuffer - ET_MESH = 1ull<<19, //!< DEPRECATED asset::ICPUMesh + ET_GEOMETRY_COLLECTION = 1ull<<18, //!< asset::ICPUGeometryCollection + ET_MORPH_TARGETS = 1ull<<19, //!< asset::ICPUMorphTargets ET_COMPUTE_PIPELINE = 1ull<<20, //!< asset::ICPUComputePipeline ET_PIPELINE_CACHE = 1ull<<21, //!< asset::ICPUPipelineCache ET_SCENE = 1ull<<22, //!< reserved, to implement later diff --git a/include/nbl/asset/IAssetManager.h b/include/nbl/asset/IAssetManager.h index 971c04d818..2105b6c4fe 100644 --- a/include/nbl/asset/IAssetManager.h +++ b/include/nbl/asset/IAssetManager.h @@ -1,9 +1,8 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_I_ASSET_MANAGER_H_INCLUDED__ -#define __NBL_ASSET_I_ASSET_MANAGER_H_INCLUDED__ +#ifndef _NBL_ASSET_I_ASSET_MANAGER_H_INCLUDED_ +#define _NBL_ASSET_I_ASSET_MANAGER_H_INCLUDED_ #include #include @@ -18,7 +17,7 @@ #include "nbl/asset/interchange/IAssetWriter.h" #include "nbl/asset/utils/CCompilerSet.h" -#include "nbl/asset/utils/IGeometryCreator.h" +#include "nbl/asset/utils/CGeometryCreator.h" #define USE_MAPS_FOR_PATH_BASED_CACHE //benchmark and choose, paths can be full system paths @@ -114,8 +113,6 @@ class NBL_API2 IAssetManager : public core::IReferenceCounted friend class IAssetLoader; friend class IAssetLoader::IAssetLoaderOverride; // for access to non-const findAssets - core::smart_refctd_ptr m_geometryCreator; - core::smart_refctd_ptr m_meshManipulator; core::smart_refctd_ptr m_compilerSet; // called as a part of constructor only void initializeMeshTools(); @@ -139,8 +136,7 @@ class NBL_API2 IAssetManager : public core::IReferenceCounted inline system::ISystem* getSystem() const { return m_system.get(); } - const IGeometryCreator* getGeometryCreator() const; - IMeshManipulator* getMeshManipulator(); + CPolygonGeometryManipulator* getPolygonGeometryManipulator(); CCompilerSet* getCompilerSet() const { return m_compilerSet.get(); } protected: diff --git a/include/nbl/asset/IBuffer.h b/include/nbl/asset/IBuffer.h index fd3ee32a26..8c3b8f95ef 100644 --- a/include/nbl/asset/IBuffer.h +++ b/include/nbl/asset/IBuffer.h @@ -100,6 +100,9 @@ struct SBufferRange inline operator SBufferRange&() {return *reinterpret_cast*>(this);} inline operator const SBufferRange&() const {return *reinterpret_cast*>(this);} + template requires std::is_same_v,BufferType> + inline operator SBufferBinding() const { return {.offset=offset,.buffer=buffer}; } + explicit inline operator bool() const {return isValid();} inline bool isValid() const diff --git a/include/nbl/asset/ICPUBuffer.h b/include/nbl/asset/ICPUBuffer.h index 46105b3c0e..2d10c2907b 100644 --- a/include/nbl/asset/ICPUBuffer.h +++ b/include/nbl/asset/ICPUBuffer.h @@ -25,6 +25,7 @@ namespace nbl::asset class ICPUBuffer final : public asset::IBuffer, public IPreHashed { public: + // TODO: template to make `data` a `const void*` vs `void*` struct SCreationParams : asset::IBuffer::SCreationParams { void* data = nullptr; diff --git a/include/nbl/asset/ICPUGeometryCollection.h b/include/nbl/asset/ICPUGeometryCollection.h new file mode 100644 index 0000000000..df135de117 --- /dev/null +++ b/include/nbl/asset/ICPUGeometryCollection.h @@ -0,0 +1,114 @@ +// Copyright (C) 2025-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_I_CPU_GEOMETRY_COLLECTION_H_INCLUDED_ +#define _NBL_ASSET_I_CPU_GEOMETRY_COLLECTION_H_INCLUDED_ + + +#include "nbl/asset/IAsset.h" +#include "nbl/asset/ICPUBuffer.h" +#include "nbl/asset/IGeometryCollection.h" + + +namespace nbl::asset +{ +// +class NBL_API2 ICPUGeometryCollection : public IAsset, public IGeometryCollection +{ + using base_t = IGeometryCollection; + + public: + inline ICPUGeometryCollection() = default; + + constexpr static inline auto AssetType = ET_GEOMETRY_COLLECTION; + inline E_TYPE getAssetType() const override {return AssetType;} + + // + inline bool valid() const //override + { + for (const auto& ref : m_geometries) + if (!ref.geometry->valid()) + return false; + return true; + } + + inline core::smart_refctd_ptr clone(uint32_t _depth=~0u) const + { + const auto nextDepth = _depth ? (_depth-1):0; + auto retval = core::smart_refctd_ptr(); + retval->m_aabb = m_aabb; + retval->m_inverseBindPoseView = m_inverseBindPoseView.clone(nextDepth); + retval->m_jointAABBView = m_jointAABBView.clone(nextDepth); + retval->m_geometries.reserve(m_geometries.size()); + for (const auto& in : m_geometries) + { + auto& out = retval->m_geometries.emplace_back(); + out.transform = in.transform; + out.geometry = core::smart_refctd_ptr_static_cast>(in.geometry->clone(nextDepth)); + out.jointRedirectView = in.jointRedirectView.clone(nextDepth); + } + return retval; + } + + // + inline bool setAABB(const IGeometryBase::SAABBStorage& aabb) + { + if (isMutable()) + { + m_aabb = aabb; + return true; + } + return false; + } + + // + inline core::vector* getGeometries() + { + if (isMutable()) + return &m_geometries; + return nullptr; + } + + // + inline bool setSkin(SDataView&& inverseBindPoseView, SDataView&& jointAABBView) + { + if (isMutable()) + return setSkin(std::move(inverseBindPoseView),std::move(jointAABBView)); + return false; + } + + // + template// requires std::is_same_v()),decltype(ICPUBottomLevelAccelerationStructure::Triangles&)> + inline Iterator exportForBLAS(Iterator out, uint32_t* pWrittenOrdinals=nullptr) const + { + return exportForBLAS(std::forward(out),[](const hlsl::float32_t3x4& lhs, const hlsl::float32_t3x4& rhs)->void + { + lhs = rhs; + if (pWrittenOrdinals) + *(pWrittenOrdinals++) = (ptrdiff_t(&rhs)-offsetof(SGeometryReference,transform)-ptrdiff_t(base_t::m_geometries.data()))/sizeof(SGeometryReference); + } + ); + } + + protected: + // + inline void visitDependents_impl(std::function visit) const override + { + auto nonNullOnly = [&visit](const IAsset* dep)->bool + { + if (dep) + return visit(dep); + return true; + }; + if (!nonNullOnly(m_inverseBindPoseView.src.buffer.get())) return; + if (!nonNullOnly(m_jointAABBView.src.buffer.get())) return; + for (const auto& ref : m_geometries) + { + const auto* geometry = static_cast(ref.geometry.get()); + if (!nonNullOnly(geometry)) return; + } + } +}; + +} +#endif \ No newline at end of file diff --git a/include/nbl/asset/ICPUMesh.h b/include/nbl/asset/ICPUMesh.h deleted file mode 100644 index df647b14a4..0000000000 --- a/include/nbl/asset/ICPUMesh.h +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_I_CPU_MESH_H_INCLUDED__ -#define __NBL_ASSET_I_CPU_MESH_H_INCLUDED__ - -#include "nbl/asset/IMesh.h" -#include "nbl/asset/IAsset.h" -#include "nbl/asset/ICPUMeshBuffer.h" - -namespace nbl -{ -namespace asset -{ - -class ICPUMesh final : public IMesh, public IAsset -{ - public: - //! These are not absolute constants, just the most common situation, there may be setups of assets/resources with completely different relationships. - _NBL_STATIC_INLINE_CONSTEXPR uint32_t MESHBUFFER_HIERARCHYLEVELS_BELOW = 1u;//mesh->meshbuffer - _NBL_STATIC_INLINE_CONSTEXPR uint32_t PIPELINE_HIERARCHYLEVELS_BELOW = MESHBUFFER_HIERARCHYLEVELS_BELOW+1u;//meshbuffer->pipeline - _NBL_STATIC_INLINE_CONSTEXPR uint32_t PIPELINE_LAYOUT_HIERARCHYLEVELS_BELOW = PIPELINE_HIERARCHYLEVELS_BELOW +1u;//meshbuffer->pipeline->layout - _NBL_STATIC_INLINE_CONSTEXPR uint32_t DESC_SET_HIERARCHYLEVELS_BELOW = MESHBUFFER_HIERARCHYLEVELS_BELOW+1u;//meshbuffer->ds - _NBL_STATIC_INLINE_CONSTEXPR uint32_t IMAGEVIEW_HIERARCHYLEVELS_BELOW = DESC_SET_HIERARCHYLEVELS_BELOW+1u;//ds->imageview - _NBL_STATIC_INLINE_CONSTEXPR uint32_t IMAGE_HIERARCHYLEVELS_BELOW = IMAGEVIEW_HIERARCHYLEVELS_BELOW+1u;//imageview->image - _NBL_STATIC_INLINE_CONSTEXPR uint32_t SAMPLER_HIERARCHYLEVELS_BELOW = DESC_SET_HIERARCHYLEVELS_BELOW+2u;//ds->layout->immutable - _NBL_STATIC_INLINE_CONSTEXPR uint32_t BUFFER_HIERARCHYLEVELS_BELOW = DESC_SET_HIERARCHYLEVELS_BELOW+1u;//ds->buffer - _NBL_STATIC_INLINE_CONSTEXPR uint32_t VTX_IDX_BUFFER_HIERARCHYLEVELS_BELOW = MESHBUFFER_HIERARCHYLEVELS_BELOW+1u;//meshbuffer->m_vertexBufferBindings or m_indexBufferBinding - - //! - inline core::SRange getMeshBuffers() const override - { - auto begin = reinterpret_cast(m_meshBuffers.data()); - return core::SRange(begin,begin+m_meshBuffers.size()); - } - inline core::SRange getMeshBuffers() override - { - assert(isMutable()); - auto begin = reinterpret_cast(m_meshBuffers.data()); - return core::SRange(begin,begin+m_meshBuffers.size()); - } - - //! Mutable access to the vector of meshbuffers - inline auto& getMeshBufferVector() - { - assert(isMutable()); - return m_meshBuffers; - } - - //! - inline const core::aabbox3df& getBoundingBox() const // needed so the compiler doesn't freak out - { - return IMesh::getBoundingBox(); - } - inline void setBoundingBox(const core::aabbox3df& newBoundingBox) override - { - assert(isMutable()); - return IMesh::setBoundingBox(newBoundingBox); - } - - - _NBL_STATIC_INLINE_CONSTEXPR auto AssetType = ET_MESH; - inline E_TYPE getAssetType() const override { return AssetType; } - - core::smart_refctd_ptr clone(uint32_t _depth = ~0u) const override - { - auto cp = core::make_smart_refctd_ptr(); - cp->m_cachedBoundingBox = m_cachedBoundingBox; - cp->m_meshBuffers.resize(m_meshBuffers.size()); - - auto outIt = cp->m_meshBuffers.begin(); - for (auto inIt=m_meshBuffers.begin(); inIt!=m_meshBuffers.end(); inIt++,outIt++) - { - if (_depth>0u && *inIt) - *outIt = core::smart_refctd_ptr_static_cast((*inIt)->clone(_depth-1u)); - else - *outIt = *inIt; - } - - return cp; - } - - inline bool valid() const override - { - for (const auto& meshBuffer : m_meshBuffers) - { - if (!meshBuffer) return false; - if (!meshBuffer->valid()) return false; - } - return true; - } - - protected: - - private: - core::vector> m_meshBuffers; - - inline void visitDependents_impl(std::function visit) const override - { - } -}; - -} -} - -#endif diff --git a/include/nbl/asset/ICPUMeshBuffer.h b/include/nbl/asset/ICPUMeshBuffer.h deleted file mode 100644 index aa6cbc9429..0000000000 --- a/include/nbl/asset/ICPUMeshBuffer.h +++ /dev/null @@ -1,626 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef _NBL_ASSET_I_CPU_MESH_BUFFER_H_INCLUDED_ -#define _NBL_ASSET_I_CPU_MESH_BUFFER_H_INCLUDED_ - -#include "nbl/asset/IMeshBuffer.h" -#include "nbl/asset/ICPUDescriptorSet.h" -#include "nbl/asset/ICPURenderpassIndependentPipeline.h" -#include "nbl/asset/format/decodePixels.h" -#include "nbl/asset/format/encodePixels.h" - -namespace nbl::asset -{ - -// TODO: This should probably go somewhere else, DEFINITELY SHOULD GO SOMEWHERE ELSE @Crisspl -namespace impl -{ - inline E_FORMAT getCorrespondingIntegerFmt(E_FORMAT _scaledFmt) - { - switch (_scaledFmt) - { - case EF_R8_USCALED: return EF_R8_UINT; - case EF_R8_SSCALED: return EF_R8_SINT; - case EF_R8G8_USCALED: return EF_R8G8_UINT; - case EF_R8G8_SSCALED: return EF_R8G8_SINT; - case EF_R8G8B8_USCALED: return EF_R8G8B8_UINT; - case EF_R8G8B8_SSCALED: return EF_R8G8B8_SINT; - case EF_B8G8R8_USCALED: return EF_B8G8R8_UINT; - case EF_B8G8R8_SSCALED: return EF_B8G8R8_SINT; - case EF_R8G8B8A8_USCALED: return EF_R8G8B8A8_UINT; - case EF_R8G8B8A8_SSCALED: return EF_R8G8B8A8_SINT; - case EF_B8G8R8A8_USCALED: return EF_B8G8R8A8_UINT; - case EF_B8G8R8A8_SSCALED: return EF_B8G8R8A8_SINT; - case EF_A8B8G8R8_USCALED_PACK32: return EF_A8B8G8R8_UINT_PACK32; - case EF_A8B8G8R8_SSCALED_PACK32: return EF_A8B8G8R8_SINT_PACK32; - case EF_A2R10G10B10_USCALED_PACK32: return EF_A2R10G10B10_UINT_PACK32; - case EF_A2R10G10B10_SSCALED_PACK32: return EF_A2R10G10B10_SINT_PACK32; - case EF_A2B10G10R10_USCALED_PACK32: return EF_A2B10G10R10_UINT_PACK32; - case EF_A2B10G10R10_SSCALED_PACK32: return EF_A2B10G10R10_SINT_PACK32; - case EF_R16_USCALED: return EF_R16_UINT; - case EF_R16_SSCALED: return EF_R16_SINT; - case EF_R16G16_USCALED: return EF_R16G16_UINT; - case EF_R16G16_SSCALED: return EF_R16G16_SINT; - case EF_R16G16B16_USCALED: return EF_R16G16B16_UINT; - case EF_R16G16B16_SSCALED: return EF_R16G16B16_SINT; - case EF_R16G16B16A16_USCALED: return EF_R16G16B16A16_UINT; - case EF_R16G16B16A16_SSCALED: return EF_R16G16B16A16_SINT; - - default: return EF_UNKNOWN; - } - } -} - -class ICPUMeshBuffer final : public IMeshBuffer, public IAsset -{ - using base_t = IMeshBuffer; - // knowing the position attribute ID is important for AABB computations etc. - uint32_t posAttrId : 5; - uint32_t normalAttrId : 5; - // by having one attribute only, we limit the number of bones per vertex to 4 - uint32_t jointIDAttrId : 5; - uint32_t jointWeightAttrId : 5; - - protected: - virtual ~ICPUMeshBuffer() = default; - - public: - //! Default constructor (initializes pipeline, desc set and buffer bindings to nullptr) - ICPUMeshBuffer() : base_t(nullptr, nullptr, nullptr, SBufferBinding{}) - { - posAttrId = 0u; - normalAttrId = MAX_VERTEX_ATTRIB_COUNT; - jointIDAttrId = MAX_VERTEX_ATTRIB_COUNT; - jointWeightAttrId = MAX_VERTEX_ATTRIB_COUNT; - } - template - ICPUMeshBuffer(Args&&... args) : base_t(std::forward(args)...) - { - posAttrId = 0u; - normalAttrId = MAX_VERTEX_ATTRIB_COUNT; - jointIDAttrId = MAX_VERTEX_ATTRIB_COUNT; - jointWeightAttrId = MAX_VERTEX_ATTRIB_COUNT; - } - - core::smart_refctd_ptr clone(uint32_t _depth = ~0u) const override - { - core::unordered_map> buffers; - auto cloneBuf = [&buffers,_depth](ICPUBuffer* buf) -> core::smart_refctd_ptr { - if (!buf) - return nullptr; - if (!_depth) - return core::smart_refctd_ptr(buf); - - auto found = buffers.find(buf); - if (found != buffers.end()) - return found->second; - - auto cp = core::smart_refctd_ptr_static_cast(buf->clone(_depth-1u)); - buffers.insert({ buf, cp }); - return cp; - }; - - auto cp = core::make_smart_refctd_ptr(); - - cp->boundingBox = boundingBox; - - cp->m_indexBufferBinding.offset = m_indexBufferBinding.offset; - cp->m_indexBufferBinding.buffer = cloneBuf(m_indexBufferBinding.buffer.get()); - for (uint32_t i = 0u; i < MAX_ATTR_BUF_BINDING_COUNT; ++i) - { - cp->m_vertexBufferBindings[i].offset = m_vertexBufferBindings[i].offset; - cp->m_vertexBufferBindings[i].buffer = cloneBuf(m_vertexBufferBindings[i].buffer.get()); - } - - cp->m_inverseBindPoseBufferBinding.offset = m_inverseBindPoseBufferBinding.offset; - cp->m_inverseBindPoseBufferBinding.buffer = cloneBuf(m_inverseBindPoseBufferBinding.buffer.get()); - cp->m_jointAABBBufferBinding.offset = m_jointAABBBufferBinding.offset; - cp->m_jointAABBBufferBinding.buffer = cloneBuf(m_jointAABBBufferBinding.buffer.get()); - - cp->m_descriptorSet = (_depth > 0u && m_descriptorSet) ? core::smart_refctd_ptr_static_cast(m_descriptorSet->clone(_depth - 1u)) : m_descriptorSet; - - memcpy(cp->m_pushConstantsData, m_pushConstantsData, sizeof(m_pushConstantsData)); - - cp->m_pipeline = (_depth > 0u && m_pipeline) ? core::smart_refctd_ptr_static_cast(m_pipeline->clone(_depth - 1u)) : m_pipeline; - - cp->indexCount = indexCount; - cp->instanceCount = instanceCount; - cp->baseVertex = baseVertex; - cp->baseInstance = baseInstance; - - cp->posAttrId = posAttrId; - cp->normalAttrId = normalAttrId; - cp->jointIDAttrId = jointIDAttrId; - cp->jointWeightAttrId = jointWeightAttrId; - - cp->jointCount = jointCount; - cp->maxJointsPerVx = maxJointsPerVx; - cp->indexType = indexType; - - return cp; - } - - _NBL_STATIC_INLINE_CONSTEXPR auto AssetType = ET_SUB_MESH; - inline E_TYPE getAssetType() const override { return AssetType; } - - inline bool setVertexBufferBinding(SBufferBinding&& bufferBinding, uint32_t bindingIndex) - { - assert(isMutable()); - if(bufferBinding.buffer) - bufferBinding.buffer->addUsageFlags(IBuffer::EUF_VERTEX_BUFFER_BIT); - return base_t::setVertexBufferBinding(std::move(bufferBinding), bindingIndex); - } - - inline void setIndexBufferBinding(SBufferBinding&& bufferBinding) - { - assert(isMutable()); - if(bufferBinding.buffer) - bufferBinding.buffer->addUsageFlags(IBuffer::EUF_INDEX_BUFFER_BIT); - return base_t::setIndexBufferBinding(std::move(bufferBinding)); - } - - //! You need to set skeleton, bind poses and AABBs all at once - inline bool setSkin( - SBufferBinding&& _inverseBindPoseBufferBinding, - SBufferBinding&& _jointAABBBufferBinding, - const uint32_t _jointCount, const uint32_t _maxJointsPerVx - ) override - { - assert(isMutable()); - - if(_inverseBindPoseBufferBinding.buffer) - _inverseBindPoseBufferBinding.buffer->addUsageFlags(IBuffer::EUF_STORAGE_BUFFER_BIT); - if(_jointAABBBufferBinding.buffer) - _jointAABBBufferBinding.buffer->addUsageFlags(IBuffer::EUF_STORAGE_BUFFER_BIT); - - return base_t::setSkin(std::move(_inverseBindPoseBufferBinding),std::move(_jointAABBBufferBinding),_jointCount,_maxJointsPerVx); - } - - //! - inline const ICPUDescriptorSet* getAttachedDescriptorSet() const - { - return base_t::getAttachedDescriptorSet(); - } - inline ICPUDescriptorSet* getAttachedDescriptorSet() - { - //assert(isMutable()); // TODO? @Crisspl? - return m_descriptorSet.get(); - } - inline void setAttachedDescriptorSet(core::smart_refctd_ptr&& descriptorSet) - { - assert(isMutable()); - base_t::setAttachedDescriptorSet(std::move(descriptorSet)); - } - - //! - inline const ICPURenderpassIndependentPipeline* getPipeline() const {return base_t::getPipeline();} - inline ICPURenderpassIndependentPipeline* getPipeline() - { - assert(isMutable()); - return m_pipeline.get(); - } - inline void setPipeline(core::smart_refctd_ptr&& pipeline) override final - { - assert(isMutable()); - base_t::setPipeline(std::move(pipeline)); - } - - // - inline uint32_t getIndexValue(uint32_t _i) const - { - if (!m_indexBufferBinding.buffer) - return _i; - switch (indexType) - { - case EIT_16BIT: - return reinterpret_cast(getIndices())[_i]; - case EIT_32BIT: - return reinterpret_cast(getIndices())[_i]; - default: - break; - } - return _i; - } - - //! Returns id of position attribute. - inline uint32_t getPositionAttributeIx() const { return posAttrId; } - //! Sets id of position atrribute. - inline void setPositionAttributeIx(const uint32_t attrId) - { - assert(isMutable()); - - if (attrId >= MAX_VERTEX_ATTRIB_COUNT) - { - #ifdef _NBL_DEBUG - //os::Printer::log("MeshBuffer setPositionAttributeIx attribute ID out of range!\n",ELL_ERROR); - #endif // _NBL_DEBUG - return; - } - - posAttrId = attrId; - } - - //! Returns id of normal attribute. - inline uint32_t getNormalAttributeIx() const { return normalAttrId; } - - //! Sets id of position atrribute. - inline void setNormalAttributeIx(const uint32_t attrId) - { - assert(isMutable()); - normalAttrId = attrId; - } - - //! Returns id of jointID attribute. - inline uint32_t getJointIDAttributeIx() const { return jointIDAttrId; } - - //! Sets id of joint atrribute. - inline void setJointIDAttributeIx(const uint32_t attrId) - { - assert(isMutable()); - jointIDAttrId = attrId; - } - - //! Returns id of joint weight attribute. - inline uint32_t getJointWeightAttributeIx() const { return jointWeightAttrId; } - - //! Sets id of joint's weight atrribute. - inline void setJointWeightAttributeIx(const uint32_t attrId) - { - assert(isMutable()); - jointWeightAttrId = attrId; - } - - //! Deduces max joint influences from the formats used for the joint attributes - inline uint32_t deduceMaxJointsPerVertex() const - { - auto safelyGetAttributeFormatChannelCount = [&](const uint32_t attrId) -> uint32_t - { - if (!isAttributeEnabled(attrId)) - return 0u; - return getFormatChannelCount(getAttribFormat(attrId)); - }; - return (core::min)(safelyGetAttributeFormatChannelCount(jointIDAttrId),safelyGetAttributeFormatChannelCount(jointWeightAttrId)+1u); - } - - //! Tells us if the mesh is skinned - inline bool isSkinned() const override - { - if (!base_t::isSkinned()) - return false; - return deduceMaxJointsPerVertex()!=0u; - } - - //! Get access to Indices. - /** \return Pointer to indices array. */ - inline void* getIndices() - { - assert(isMutable()); - - if (!m_indexBufferBinding.buffer) - return nullptr; - - return reinterpret_cast(m_indexBufferBinding.buffer->getPointer()) + m_indexBufferBinding.offset; - } - - //! Get access to Indices. - /** We only keep track of a position attribute, as every vertex needs to have at least a position to be displayed on the screen. - Certain vertices may not have colors, normals, texture coords, etc. but a position is always present. - \return Pointer to index array. */ - inline const void* getIndices() const - { - if (!m_indexBufferBinding.buffer) - return nullptr; - - return reinterpret_cast(m_indexBufferBinding.buffer->getPointer()) + m_indexBufferBinding.offset; - } - - //! Accesses given index of mapped position attribute buffer. - /** @param ix Index number of vertex which is to be returned. - @returns `ix`th vertex of mapped attribute buffer or (0, 0, 0, 1) vector if an error occured (e.g. no such vertex). - @see @ref getAttribute() - */ - virtual core::vectorSIMDf getPosition(size_t ix) const - { - core::vectorSIMDf outPos(0.f, 0.f, 0.f, 1.f); - bool success = getAttribute(outPos, posAttrId, ix); - #ifdef _NBL_DEBUG - if (!success) - { - //os::Printer::log("SOME DEBUG MESSAGE!\n",ELL_ERROR); - } - #endif // _NBL_DEBUG - return outPos; - } - - //! Accesses data of buffer of attribute of given id - /** Basically it will get the start of the array at the same point as OpenGL will get upon a glDraw*. - @param attrId Attribute id. - @returns Pointer to corresponding buffer's data incremented by `baseVertex` and by `bufferOffset` - @see @ref getBaseVertex() setBaseVertex() getAttribute() - */ - virtual uint8_t* getAttribPointer(uint32_t attrId) - { - assert(isMutable()); - - if (!m_pipeline) - return nullptr; - - const auto& cachedParams = m_pipeline->getCachedCreationParams(); - const auto& vtxInputParams = cachedParams.vertexInput; - if (!isAttributeEnabled(attrId)) - return nullptr; - - const uint32_t bindingNum = vtxInputParams.attributes[attrId].binding; - if (!isVertexAttribBufferBindingEnabled(bindingNum)) - return nullptr; - - ICPUBuffer* mappedAttrBuf = m_vertexBufferBindings[bindingNum].buffer.get(); - if (!mappedAttrBuf) - return nullptr; - - int64_t ix = vtxInputParams.bindings[bindingNum].inputRate!=SVertexInputBindingParams::EVIR_PER_VERTEX ? baseInstance:baseVertex; - ix *= vtxInputParams.bindings[bindingNum].stride; - ix += (m_vertexBufferBindings[bindingNum].offset + vtxInputParams.attributes[attrId].relativeOffset); - if (ix < 0 || static_cast(ix) >= mappedAttrBuf->getSize()) - return nullptr; - - return reinterpret_cast(mappedAttrBuf->getPointer()) + ix; - } - inline const uint8_t* getAttribPointer(uint32_t attrId) const - { - return const_cast::type&>(*this).getAttribPointer(attrId); - } - - static inline bool getAttribute(core::vectorSIMDf& output, const void* src, E_FORMAT format) - { - if (!src) - return false; - - bool scaled = false; - if (!isNormalizedFormat(format) && !isFloatingPointFormat(format) && !(scaled = isScaledFormat(format))) - return false; - - if (!scaled) - { - double output64[4]{ 0., 0., 0., 1. }; - decodePixels(format, &src, output64, 0u, 0u); - for (auto i=0u; i<4u; i++) - output[i] = static_cast(output64[i]); - } - else - { - if (isSignedFormat(format)) - { - int64_t output64i[4]{ 0, 0, 0, 1 }; - decodePixels(impl::getCorrespondingIntegerFmt(format), &src, output64i, 0u, 0u); - for (auto i=0u; i<4u; i++) - output[i] = static_cast(output64i[i]); - } - else - { - uint64_t output64u[4]{ 0u, 0u, 0u, 1u }; - decodePixels(impl::getCorrespondingIntegerFmt(format), &src, output64u, 0u, 0u); - for (auto i=0u; i<4u; i++) - output[i] = static_cast(output64u[i]); - } - } - - return true; - } - - //! Accesses vertex of given index of given vertex attribute. Index number is incremented by `baseVertex`. WARNING: NOT ALL FORMAT CONVERSIONS TO RGBA32F/XYZW32F ARE IMPLEMENTED! - /** If component count of given attribute is less than 4, only first ones of output vector's members will be written. - @param[out] output vectorSIMDf object to which index's value will be returned. - @param[in] attrId Atrribute id. - @param[in] ix Index which is to be accessed. Will be incremented by `baseVertex`. - @returns true if successful or false if an error occured (e.g. `ix` out of range, no attribute specified/bound or given attribute's format conversion to vectorSIMDf unsupported). - @see @ref getBaseVertex() setBaseVertex() getAttribute() - */ - virtual bool getAttribute(core::vectorSIMDf& output, uint32_t attrId, size_t ix) const - { - if (!isAttributeEnabled(attrId)) - return false; - - const uint32_t bindingId = getBindingNumForAttribute(attrId); - - const uint8_t* src = getAttribPointer(attrId); - src += ix * getAttribStride(attrId); - if (src >= reinterpret_cast(m_vertexBufferBindings[bindingId].buffer->getPointer()) + m_vertexBufferBindings[bindingId].buffer->getSize()) - return false; - - return getAttribute(output, src, getAttribFormat(attrId)); - } - - static inline bool getAttribute(uint32_t* output, const void* src, E_FORMAT format) - { - if (!src) - return false; - - bool scaled = false; - if ((scaled = isScaledFormat(format)) || isIntegerFormat(format)) - { - if (isSignedFormat(format)) - { - int64_t output64[4]{0, 0, 0, 1}; - decodePixels(scaled ? impl::getCorrespondingIntegerFmt(format) : format, &src, output64, 0u, 0u); - for (uint32_t i = 0u; i < getFormatChannelCount(format); ++i) - output[i] = static_cast(output64[i]); - } - else - { - uint64_t output64[4]{0u, 0u, 0u, 1u}; - decodePixels(scaled ? impl::getCorrespondingIntegerFmt(format) : format, &src, output64, 0u, 0u); - for (uint32_t i = 0u; i < getFormatChannelCount(format); ++i) - output[i] = static_cast(output64[i]); - } - return true; - } - - return false; - } - - //! Accesses vertex of given index of given vertex attribute. Index number is incremented by `baseVertex`. WARNING: NOT ALL FORMAT CONVERSIONS TO RGBA32F/XYZW32F ARE IMPLEMENTED! - /** If component count of given attribute is less than 4, only first ones of output vector's members will be written. - Attributes of integer types smaller than 32 bits are promoted to 32bit integer. - @param[out] output Pointer to memory to which index's value will be returned. - @param[in] attrId Atrribute id. - @param[in] ix Index which is to be accessed. Will be incremented by `baseVertex`. - @returns true if successful or false if an error occured (e.g. `ix` out of range, no attribute specified/bound or given attribute's format conversion to vectorSIMDf unsupported). - @see @ref getBaseVertex() setBaseVertex() getAttribute() - */ - virtual bool getAttribute(uint32_t* output, uint32_t attrId, size_t ix) const - { - if (!m_pipeline) - return false; - if (!isAttributeEnabled(attrId)) - return false; - - const uint8_t* src = getAttribPointer(attrId); - src += ix * getAttribStride(attrId); - const ICPUBuffer* buf = base_t::getAttribBoundBuffer(attrId).buffer.get(); - if (!buf || src >= reinterpret_cast(buf->getPointer()) + buf->getSize()) - return false; - - return getAttribute(output, src, getAttribFormat(attrId)); - } - - static inline bool setAttribute(core::vectorSIMDf input, void* dst, E_FORMAT format) - { - bool scaled = false; - if (!dst || (!isFloatingPointFormat(format) && !isNormalizedFormat(format) && !(scaled = isScaledFormat(format)))) - return false; - - double input64[4]; - for (uint32_t i = 0u; i < 4u; ++i) - input64[i] = input.pointer[i]; - - if (!scaled) - encodePixels(format, dst, input64); - else - { - if (isSignedFormat(format)) - { - int64_t input64i[4]{ static_cast(input64[0]), static_cast(input64[1]), static_cast(input64[2]), static_cast(input64[3]) }; - encodePixels(impl::getCorrespondingIntegerFmt(format), dst, input64i); - } - else - { - uint64_t input64u[4]{ static_cast(input64[0]), static_cast(input64[1]), static_cast(input64[2]), static_cast(input64[3]) }; - encodePixels(impl::getCorrespondingIntegerFmt(format), dst, input64u); - } - } - - return true; - } - - //! Sets value of vertex of given index of given attribute. WARNING: NOT ALL FORMAT CONVERSIONS FROM RGBA32F/XYZW32F (vectorSIMDf) ARE IMPLEMENTED! - /** @param input Value which is to be set. - @param attrId Atrribute id. - @param ix Index of vertex which is to be set. Will be incremented by `baseVertex`. - @returns true if successful or false if an error occured (e.g. no such index). - @see @ref getBaseVertex() setBaseVertex() getAttribute() - */ - virtual bool setAttribute(core::vectorSIMDf input, uint32_t attrId, size_t ix) - { - assert(isMutable()); - if (!m_pipeline) - return false; - if (!isAttributeEnabled(attrId)) - return false; - - uint8_t* dst = getAttribPointer(attrId); - dst += ix * getAttribStride(attrId); - ICPUBuffer* buf = getAttribBoundBuffer(attrId).buffer.get(); - if (!buf || dst >= ((const uint8_t*)(buf->getPointer())) + buf->getSize()) - return false; - - return setAttribute(input, dst, getAttribFormat(attrId)); - } - - static inline bool setAttribute(const uint32_t* _input, void* dst, E_FORMAT format) - { - const bool scaled = isScaledFormat(format); - if (!dst || !(scaled || isIntegerFormat(format))) - return false; - uint8_t* vxPtr = reinterpret_cast(dst); - - if (isSignedFormat(format)) - { - int64_t input[4]; - for (uint32_t i = 0u; i < 4u; ++i) - input[i] = reinterpret_cast(_input)[i]; - encodePixels(scaled ? impl::getCorrespondingIntegerFmt(format) : format, dst, input); - } - else - { - uint64_t input[4]; - for (uint32_t i = 0u; i < 4u; ++i) - input[i] = _input[i]; - encodePixels(scaled ? impl::getCorrespondingIntegerFmt(format) : format, dst, input); - } - return true; - } - - //! @copydoc setAttribute(core::vectorSIMDf, const E_VERTEX_ATTRIBUTE_ID, size_t) - virtual bool setAttribute(const uint32_t* _input, uint32_t attrId, size_t ix) - { - assert(isMutable()); - if (!m_pipeline) - return false; - if (!isAttributeEnabled(attrId)) - return false; - - uint8_t* dst = getAttribPointer(attrId); - dst += ix * getAttribStride(attrId); - ICPUBuffer* buf = getAttribBoundBuffer(attrId).buffer.get(); - if (dst >= ((const uint8_t*)(buf->getPointer())) + buf->getSize()) - return false; - - return setAttribute(_input, dst, getAttribFormat(attrId)); - } - - //! - inline const core::matrix3x4SIMD* getInverseBindPoses() const - { - if (!m_inverseBindPoseBufferBinding.buffer) - return nullptr; - - const uint8_t* ptr = reinterpret_cast(m_inverseBindPoseBufferBinding.buffer->getPointer()); - return reinterpret_cast(ptr+m_inverseBindPoseBufferBinding.offset); - } - inline core::matrix3x4SIMD* getInverseBindPoses() - { - assert(isMutable()); - return const_cast(const_cast(this)->getInverseBindPoses()); - } - - //! - inline const core::aabbox3df* getJointAABBs() const - { - if (!m_jointAABBBufferBinding.buffer) - return nullptr; - - const uint8_t* ptr = reinterpret_cast(m_jointAABBBufferBinding.buffer->getPointer()); - return reinterpret_cast(ptr+ m_jointAABBBufferBinding.offset); - } - inline core::aabbox3df* getJointAABBs() - { - assert(isMutable()); - return const_cast(const_cast(this)->getJointAABBs()); - } - inline bool valid() const override - { - return true; - } - - private: - inline void visitDependents_impl(std::function visit) const override - { - } -}; - -} - -#endif diff --git a/include/nbl/asset/ICPUMorphTargets.h b/include/nbl/asset/ICPUMorphTargets.h new file mode 100644 index 0000000000..545d2cd8a9 --- /dev/null +++ b/include/nbl/asset/ICPUMorphTargets.h @@ -0,0 +1,72 @@ +// Copyright (C) 2025-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_I_CPU_MORPH_TARGETS_H_INCLUDED_ +#define _NBL_ASSET_I_CPU_MORPH_TARGETS_H_INCLUDED_ + + +#include "nbl/asset/IAsset.h" +#include "nbl/asset/IMorphTargets.h" + + +namespace nbl::asset +{ +// +class NBL_API2 ICPUMorphTargets : public IAsset, public IMorphTargets +{ + using base_t = IMorphTargets; + + public: + inline ICPUMorphTargets() = default; + + constexpr static inline auto AssetType = ET_MORPH_TARGETS; + inline E_TYPE getAssetType() const override {return AssetType;} + + // + inline bool valid() const //override + { + for (const auto& target : m_targets) + if (!target || !target.geoCollection->valid()) + return false; + return true; + } + + inline core::smart_refctd_ptr clone(uint32_t _depth=~0u) const + { + const auto nextDepth = _depth ? (_depth-1):0; + auto retval = core::smart_refctd_ptr(); + retval->m_targets.reserve(m_targets.size()); + for (const auto& in : m_targets) + { + auto& out = retval->m_targets.emplace_back(); + out.geoCollection = core::smart_refctd_ptr_static_cast(in.geoCollection->clone(nextDepth)); + out.jointRedirectView = in.jointRedirectView.clone(nextDepth); + } + return retval; + } + + // + inline core::vector* getTargets() + { + if (isMutable()) + return &m_targets; + return nullptr; + } + + protected: + // + inline void visitDependents_impl(std::function visit) const //override + { + auto nonNullOnly = [&visit](const IAsset* dep)->bool + { + if (dep) + return visit(dep); + return true; + }; + for (const auto& ref : m_targets) + if (!nonNullOnly(ref.geoCollection.get())) return; + } +}; + +} +#endif \ No newline at end of file diff --git a/include/nbl/asset/ICPUPolygonGeometry.h b/include/nbl/asset/ICPUPolygonGeometry.h new file mode 100644 index 0000000000..2119bb5ace --- /dev/null +++ b/include/nbl/asset/ICPUPolygonGeometry.h @@ -0,0 +1,179 @@ +// Copyright (C) 2025-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_I_CPU_POLYGON_GEOMETRY_H_INCLUDED_ +#define _NBL_ASSET_I_CPU_POLYGON_GEOMETRY_H_INCLUDED_ + + +#include "nbl/asset/IAsset.h" +#include "nbl/asset/ICPUBuffer.h" +#include "nbl/asset/IPolygonGeometry.h" + + +namespace nbl::asset +{ +// +class NBL_API2 ICPUPolygonGeometry final : public IPolygonGeometry +{ + using base_t = IPolygonGeometry; + + public: + using SDataView = base_t::SDataView; + + inline ICPUPolygonGeometry() = default; + + constexpr static inline auto AssetType = ET_GEOMETRY; + inline E_TYPE getAssetType() const override {return AssetType;} + + inline bool valid() const override {return base_t::valid();} + + inline core::smart_refctd_ptr clone(uint32_t _depth=~0u) const override + { + const auto nextDepth = _depth ? (_depth-1):0; + auto retval = core::smart_refctd_ptr(); + retval->m_positionView = m_positionView.clone(nextDepth); + retval->m_jointOBBView = m_jointOBBView.clone(nextDepth); + retval->m_indexView = m_indexView.clone(nextDepth); + retval->m_jointWeightViews.reserve(m_jointWeightViews.size()); + for (const auto& pair : m_jointWeightViews) + retval->m_jointWeightViews.push_back({ + .indices = pair.indices.clone(nextDepth), + .weights = pair.weights.clone(nextDepth) + }); + retval->m_auxAttributeViews.reserve(m_auxAttributeViews.size()); + for (const auto& view : m_auxAttributeViews) + retval->m_auxAttributeViews.push_back(view.clone(nextDepth)); + retval->m_normalView = m_normalView.clone(nextDepth); + retval->m_jointCount = m_jointCount; + return retval; + } + + // + inline bool setPositionView(SDataView&& view) + { + if (isMutable() && (!view || view.composed.isFormatted())) + { + m_positionView = std::move(view); + return true; + } + return false; + } + + // + inline bool setJointOBBView(SDataView&& view) + { + if (isMutable()) + return base_t::setJointOBBView(std::move(view)); + return false; + } + + // + inline bool setIndexView(SDataView&& view) + { + if (isMutable()) + return base_t::setIndexView(std::move(view)); + return false; + } + + // + inline bool setIndexing(const IIndexingCallback* indexing) + { + if (isMutable()) + { + m_indexing = indexing; + return true; + } + return false; + } + // + inline bool setNormalView(SDataView&& view) + { + if (isMutable() && (!view || view.composed.getStride()>0)) + { + m_normalView = std::move(view); + return true; + } + return false; + } + + // + inline bool setJointCount(const uint32_t count) + { + if (isMutable()) + { + m_jointCount = count; + return true; + } + return false; + } + + // + inline const core::vector& getJointWeightViews() const {return base_t::getJointWeightViews();} + inline core::vector* getJointWeightViews() + { + if (isMutable()) + return &m_jointWeightViews; + return nullptr; + } + + // + inline const core::vector& getAuxAttributeViews() const {return base_t::getAuxAttributeViews();} + inline core::vector* getAuxAttributeViews() + { + if (isMutable()) + return &m_auxAttributeViews; + return nullptr; + } + + // We don't care about primitive restart, you need to check for it yourself. + // Unlike OpenGL and other APIs we don't adjust the Primitive ID because that breaks parallel processing. + // So a triangle strip `{ 0 1 2 3 RESTART 2 3 4 5 }` means 7 primitives, of which 3 are invalid (contain the restart index) + template requires hlsl::concepts::UnsignedIntegralScalar + inline bool getPrimitiveIndices(Out* out, const uint32_t beginPrimitive, const uint32_t endPrimitive) const + { + if (!m_indexing) + return false; + IIndexingCallback::SContext ctx = { + .indexBuffer = m_indexView.getPointer(), + .indexSize = getTexelOrBlockBytesize(m_indexView.composed.format), + .beginPrimitive = beginPrimitive, + .endPrimitive = endPrimitive, + .out = out + }; + m_indexing->operator()(ctx); + return true; + } + + // + template + inline bool getPrimitiveIndices(Out* out, const uint32_t primitiveID) const + { + return getPrimitiveIndices(out,primitiveID,primitiveID+1); + } + + protected: + // + inline void visitDependents_impl(std::function visit) const override + { + auto nonNullOnly = [&visit](const IAsset* dep)->bool + { + if (dep) + return visit(dep); + return true; + }; + if (!nonNullOnly(m_positionView.src.buffer.get())) return; + if (!nonNullOnly(m_jointOBBView.src.buffer.get())) return; + if (!nonNullOnly(m_indexView.src.buffer.get())) return; + for (const auto& pair : m_jointWeightViews) + { + if (!nonNullOnly(pair.indices.src.buffer.get())) return; + if (!nonNullOnly(pair.weights.src.buffer.get())) return; + } + for (const auto& view : m_auxAttributeViews) + if (!nonNullOnly(view.src.buffer.get())) return; + if (!nonNullOnly(m_normalView.src.buffer.get())) return; + } +}; + +} +#endif \ No newline at end of file diff --git a/include/nbl/asset/ICPURenderpassIndependentPipeline.h b/include/nbl/asset/ICPURenderpassIndependentPipeline.h deleted file mode 100644 index 3d67af23d0..0000000000 --- a/include/nbl/asset/ICPURenderpassIndependentPipeline.h +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright (C) 2018-2023 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h -#ifndef _NBL_ASSET_I_CPU_RENDERPASS_INDEPENDENT_PIPELINE_H_INCLUDED_ -#define _NBL_ASSET_I_CPU_RENDERPASS_INDEPENDENT_PIPELINE_H_INCLUDED_ - -#include "nbl/asset/IRenderpassIndependentPipeline.h" -#include "nbl/asset/ICPUPipelineLayout.h" -#include "nbl/asset/IShader.h" - -namespace nbl::asset -{ - -//! CPU Version of Renderpass Independent Pipeline -/* - @see IRenderpassIndependentPipeline -*/ - -class ICPURenderpassIndependentPipeline : public IRenderpassIndependentPipeline, public IAsset -{ - public: - struct SCreationParams - { - std::span shaders = {}; - SCachedCreationParams cached = {}; - }; - - //(TODO) it is true however it causes DSs to not be cached when ECF_DONT_CACHE_TOP_LEVEL is set which isnt really intuitive - constexpr static inline uint32_t DESC_SET_HIERARCHYLEVELS_BELOW = 0u; - // TODO: @Crisspl HOW ON EARTH DOES THIS MAKE SENSE!? - constexpr static inline uint32_t IMAGEVIEW_HIERARCHYLEVELS_BELOW = 1u; - constexpr static inline uint32_t IMAGE_HIERARCHYLEVELS_BELOW = 2u; - // from here its good - constexpr static inline uint32_t PIPELINE_LAYOUT_HIERARCHYLEVELS_BELOW = 1u; - constexpr static inline uint32_t DESC_SET_LAYOUT_HIERARCHYLEVELS_BELOW = 1u+ICPUPipelineLayout::DESC_SET_LAYOUT_HIERARCHYLEVELS_BELOW; - constexpr static inline uint32_t IMMUTABLE_SAMPLER_HIERARCHYLEVELS_BELOW = 1u+ICPUPipelineLayout::IMMUTABLE_SAMPLER_HIERARCHYLEVELS_BELOW; - constexpr static inline uint32_t SPECIALIZED_SHADER_HIERARCHYLEVELS_BELOW = 1u; - - static core::smart_refctd_ptr create(core::smart_refctd_ptr&& _layout, const SCreationParams& params) - { - // we'll validate the specialization info later when attempting to set it - if (!_layout || params.shaders.empty()) - return nullptr; - auto retval = new ICPURenderpassIndependentPipeline(std::move(_layout),params.cached); -#if 0 - for (const auto spec : params.shaders) - if (spec.shader) - retval->setSpecInfo(spec); -#endif - return core::smart_refctd_ptr(retval,core::dont_grab); - } - - // IAsset implementations - core::smart_refctd_ptr clone(const uint32_t _depth = ~0u) const override - { - core::smart_refctd_ptr layout; - if (_depth>0u && m_layout) - layout = core::smart_refctd_ptr_static_cast(m_layout->clone(_depth-1u)); - else - layout = m_layout; - - auto cp = new ICPURenderpassIndependentPipeline(std::move(layout),m_cachedParams); -#if 0 - for (const auto spec : m_infos) - if (spec.shader) - cp->setSpecInfo(spec); -#endif - - return core::smart_refctd_ptr(cp,core::dont_grab); - } - - _NBL_STATIC_INLINE_CONSTEXPR auto AssetType = ET_RENDERPASS_INDEPENDENT_PIPELINE; - inline E_TYPE getAssetType() const override { return AssetType; } - - // - inline const SCachedCreationParams& getCachedCreationParams() const {return IRenderpassIndependentPipeline::getCachedCreationParams();} - inline SCachedCreationParams& getCachedCreationParams() - { - assert(isMutable()); - return m_cachedParams; - } - - // extras for this class - inline ICPUPipelineLayout* getLayout() - { - assert(isMutable()); - return m_layout.get(); - } - const inline ICPUPipelineLayout* getLayout() const { return m_layout.get(); } - inline void setLayout(core::smart_refctd_ptr&& _layout) - { - assert(isMutable()); - m_layout = std::move(_layout); - } - - inline bool valid() const override - { - return m_layout && m_layout->valid(); - } - -#if 0 - // The getters are weird because the shader pointer needs patching - inline IShader::SSpecInfo getSpecInfos(const hlsl::ShaderStage stage) - { - assert(isMutable()); - const auto stageIx = hlsl::findLSB(stage); - if (stageIx<0 || stageIx>=GRAPHICS_SHADER_STAGE_COUNT || hlsl::bitCount(stage)!=1) - return {}; - return m_infos[stageIx]; - } - inline IShader::SSpecInfo getSpecInfos(const hlsl::ShaderStage stage) const - { - const auto stageIx = hlsl::findLSB(stage); - if (stageIx<0 || stageIx>=GRAPHICS_SHADER_STAGE_COUNT || hlsl::bitCount(stage)!=1) - return {}; - return m_infos[stageIx]; - } - inline bool setSpecInfo(const IShader::SSpecInfo& info) - { - assert(isMutable()); - const int64_t specSize = info.valid(); - if (specSize<0) - return false; - const auto stage = info.shader->getStage(); - const auto stageIx = hlsl::findLSB(stage); - if (stageIx<0 || stageIx>=GRAPHICS_SHADER_STAGE_COUNT || hlsl::bitCount(stage)!=1) - return false; - m_infos[stageIx] = info; - m_shaders[stageIx] = core::smart_refctd_ptr(info.shader); - m_infos[stageIx].shader = m_shaders[stageIx].get(); - if (specSize>0) - { - m_entries[stageIx] = std::make_unique(); - m_entries[stageIx]->reserve(info.entries->size()); - std::copy(info.entries->begin(),info.entries->end(),std::insert_iterator(*m_entries[stageIx],m_entries[stageIx]->begin())); - } - else - m_entries[stageIx] = nullptr; - m_infos[stageIx].entries = m_entries[stageIx].get(); - return true; - } -#endif - - protected: - ICPURenderpassIndependentPipeline(core::smart_refctd_ptr&& _layout, const IRenderpassIndependentPipeline::SCachedCreationParams& params) - : IRenderpassIndependentPipeline(params), m_layout(std::move(_layout)) {} - virtual ~ICPURenderpassIndependentPipeline() = default; - - core::smart_refctd_ptr m_layout; -#if 0 - std::array,GRAPHICS_SHADER_STAGE_COUNT> m_shaders = {}; - std::array,GRAPHICS_SHADER_STAGE_COUNT> m_entries = {}; - std::array m_infos = {}; -#endif - - private: - - inline void visitDependents_impl(std::function visit) const override - { - } -}; - -} -#endif diff --git a/include/nbl/asset/IGeometry.h b/include/nbl/asset/IGeometry.h new file mode 100644 index 0000000000..b6330f79bc --- /dev/null +++ b/include/nbl/asset/IGeometry.h @@ -0,0 +1,426 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_I_GEOMETRY_H_INCLUDED_ +#define _NBL_ASSET_I_GEOMETRY_H_INCLUDED_ + + +#include "nbl/builtin/hlsl/shapes/aabb.hlsl" + +#include "nbl/asset/IAsset.h" + + +namespace nbl::asset +{ +class IGeometryBase : public virtual core::IReferenceCounted +{ + public: + // used for same purpose as and overlaps `IAsset::valid()` + virtual bool valid() const = 0; + + enum class EPrimitiveType : uint8_t + { + Polygon = 0, + // do not overengineer +// AABBs = 1, + // LSS, Beziers etc. + }; + // + virtual EPrimitiveType getPrimitiveType() const = 0; + + // + enum class EAABBFormat : uint8_t + { + F64, + U64, + S64, + F32, + U32, + S32, + F16, + U16, + U16_NORM, + S16, + S16_NORM, + U8, + U8_NORM, + S8, + S8_NORM, + BitCount=4 + }; + // using `nbl::hlsl::` concepts instead of `std::` so that `nbl::hlsl::float16_t` can be used + union SAABBStorage + { + hlsl::shapes::AABB<4,hlsl::float64_t> f64 = hlsl::shapes::AABB<4,hlsl::float64_t>::create(); + hlsl::shapes::AABB<4,uint64_t> u64; + hlsl::shapes::AABB<4,int64_t> s64; + hlsl::shapes::AABB<4,hlsl::float32_t> f32; + hlsl::shapes::AABB<4,uint32_t> u32; + hlsl::shapes::AABB<4,int32_t> s32; + hlsl::shapes::AABB<4,hlsl::float16_t> f16; + hlsl::shapes::AABB<4,uint16_t> u16; + hlsl::shapes::AABB<4,int16_t> s16; + hlsl::shapes::AABB<4,uint8_t> u8; + hlsl::shapes::AABB<4,int8_t> s8; + }; + struct SDataViewBase + { + // mostly checking validity of the format + inline operator bool() const {return format==EF_UNKNOWN || !isBlockCompressionFormat(format) && !isDepthOrStencilFormat(format);} + + // + inline bool isFormatted() const {return format!=EF_UNKNOWN && bool(*this);} + + // Useful for checking if something can be used as an index + inline bool isFormattedScalarInteger() const + { + if (isFormatted()) + switch (format) + { + case EF_R8_SINT: [[fallthrough]]; + case EF_R8_UINT: [[fallthrough]]; + case EF_R16_SINT: [[fallthrough]]; + case EF_R16_UINT: [[fallthrough]]; + case EF_R32_SINT: [[fallthrough]]; + case EF_R32_UINT: [[fallthrough]]; + case EF_R64_SINT: [[fallthrough]]; + case EF_R64_UINT: + return true; + default: + break; + } + return false; + } + + // + inline uint32_t getStride() const + { + if (isFormatted()) + return getFormatClassBlockBytesize(getFormatClass(format)); + return stride; + } + + // + template + inline void visitAABB(Visitor& visitor) + { + switch (rangeFormat) + { + case EAABBFormat::F64: + visitor(encodedDataRange.f64); + break; + case EAABBFormat::U64: + visitor(encodedDataRange.u64); + break; + case EAABBFormat::S64: + visitor(encodedDataRange.s64); + break; + case EAABBFormat::F32: + visitor(encodedDataRange.f32); + break; + case EAABBFormat::U32: + visitor(encodedDataRange.u32); + break; + case EAABBFormat::S32: + visitor(encodedDataRange.s32); + break; + case EAABBFormat::F16: + visitor(encodedDataRange.f16); + break; + case EAABBFormat::U16: [[fallthrough]]; + case EAABBFormat::U16_NORM: + visitor(encodedDataRange.u16); + break; + case EAABBFormat::S16: [[fallthrough]]; + case EAABBFormat::S16_NORM: + visitor(encodedDataRange.s16); + break; + case EAABBFormat::U8: [[fallthrough]]; + case EAABBFormat::U8_NORM: + visitor(encodedDataRange.u8); + break; + case EAABBFormat::S8: [[fallthrough]]; + case EAABBFormat::S8_NORM: + visitor(encodedDataRange.s8); + break; + default: + break; + } + } + template + inline void visitAABB(const Visitor& visitor) const + { + auto tmp = [&visitor](const auto& aabb)->void{visitor(aabb);}; + const_cast*>(this)->visitAABB(tmp); + } + + // + inline void resetRange(const EAABBFormat newFormat) + { + rangeFormat = newFormat; + auto tmp = [](auto& aabb)->void{aabb = aabb.create();}; + visitAABB(tmp); + } + inline void resetRange() {resetRange(rangeFormat);} + + // + template + inline AABB getRange() const + { + AABB retval = AABB::create(); + auto tmp = [&retval](const auto& aabb)->void + { + retval.minVx = aabb.minVx; + retval.maxVx = aabb.maxVx; + }; + visitAABB(tmp); + return retval; + } + + // optional, really only meant for formatted views + SAABBStorage encodedDataRange = {}; + // 0 means no fixed stride, totally variable data inside + uint32_t stride = 0; + // Format takes precedence over stride + // Note :If format is UNORM or SNORM, the vertex data is relative to the AABB (range) + E_FORMAT format = EF_UNKNOWN; + // tells you which `encodedDataRange` union member to access + EAABBFormat rangeFormat : int(EAABBFormat::BitCount) = EAABBFormat::F64; + }; + + virtual const SAABBStorage& getAABB() const = 0; + + protected: + virtual inline ~IGeometryBase() = default; +}; + +// a thing to expose `clone()` conditionally via inheritance and `conditional_t` +namespace impl +{ +class NBL_FORCE_EBO NBL_NO_VTABLE INotCloneable {}; +} + +// A geometry should map 1:1 to a BLAS geometry, Meshlet or a Drawcall in API terms +template +class IGeometry : public std::conditional_t,IAsset,impl::INotCloneable>, public IGeometryBase +{ + public: + // + virtual inline bool valid() const override + { + if (!m_positionView) + return false; + if (getPrimitiveCount()==0) + return false; + // joint OBBs are optional + return true; + } + + struct SDataView + { + inline operator bool() const {return src && composed;} + + // + explicit inline operator SBufferBinding() const + { + if (*this) + return {.offset=src.offset,.buffer=smart_refctd_ptr(src.buffer)}; + return {}; + } + + inline uint64_t getElementCount() const + { + if (!this->operator bool()) + return 0ull; + const auto stride = composed.getStride(); + if (stride==0) + return 0ull; + return src.size/stride; + } + + // + template requires (std::is_same_v && std::is_same_v) + inline const void* getPointer(const Index elIx=0) const + { + return const_cast*>(this)->getPointer(elIx); + } + template requires (std::is_same_v && std::is_same_v) + inline void* getPointer(const Index elIx=0) + { + if (*this) + return reinterpret_cast(src.buffer->getPointer())+src.offset+elIx*composed.getStride(); + return nullptr; + } + + // + template requires (hlsl::concepts::Vector && std::is_same_v && std::is_same_v) + inline bool decodeElement(const Index elIx, V& v) const + { + if (!composed.isFormatted()) + return false; + using code_t = std::conditional_t,hlsl::float64_t,std::conditional_t,int64_t,uint64_t>>; + code_t tmp[4]; + if (const auto* src=getPointer(elIx); src) + { + const void* srcArr[4] = {src,nullptr}; + assert(!isScaledFormat(composed.format)); // handle this by improving the decode functions, not adding workarounds here + if (decodePixels(composed.format,srcArr,tmp,0,0)) + { + if (isNormalizedFormat(composed.format)) + { + using traits = hlsl::vector_traits; + const auto range = composed.getRange>(); + v = v*(range.maxVx-range.minVx)+range.minVx; + } + return true; + } + } + return false; + } + + // + template requires (hlsl::concepts::Vector && std::is_same_v && std::is_same_v) + inline void encodeElement(const Index elIx, const V& v) + { + if (!composed.isFormatted()) + return false; + void* const out = getPointer(elIx); + if (!out) + return false; + using traits = hlsl::vector_traits; + using code_t = std::conditional_t,hlsl::float64_t,std::conditional_t,int64_t,uint64_t>>; + code_t tmp[traits::Dimension]; + const auto range = composed.getRange(); + for (auto i=0u; i(composed.format,out,tmp)) + return true; + return false; + } + + // + inline SDataView clone(uint32_t _depth=~0u) const + { + SDataView retval; + retval.composed = composed; + retval.src.offset = src.offset; + retval.src.size = src.size; + if (_depth) + retval.src.buffer = core::smart_refctd_ptr_static_cast(src.buffer->clone(_depth)); + else + retval.src.buffer = core::smart_refctd_ptr(src.buffer); + return retval; + } + + SDataViewBase composed = {}; + SBufferRange src = {}; + }; + // + inline const SDataView& getPositionView() const {return m_positionView;} + + // depends on indexing, primitive type, etc. + virtual uint64_t getPrimitiveCount() const = 0; + + // This is the upper bound on the local Joint IDs that the geometry uses + virtual uint32_t getJointCount() const = 0; + + // + inline bool isSkinned() const {return getJointCount()>0;} + + // Providing Per-Joint Bind-Pose-Space AABBs is optional for a skinned geometry + inline const SDataView* getJointOBBView() const + { + if (isSkinned()) + return &m_jointOBBView; + return nullptr; + } + + protected: + virtual inline ~IGeometry() = default; + + // + inline bool setJointOBBView(SDataView&& view) + { + // want to set, not clear the AABBs + if (view) + { + // An OBB is a affine transform of a [0,1]^3 unit cube to the Oriented Bounding Box + // Needs to be formatted, each element is a row of that matrix + if (!view.composed.isFormatted() || getFormatChannelCount(view.composed.format)!=4) + return false; + // The range doesn't need to be exact, just large enough + if (view.getElementCount() +class IIndexableGeometry : public IGeometry +{ + protected: + using SDataView = IGeometry::SDataView; + + public: + // index buffer is optional so no override of `valid()` + + inline const SDataView& getIndexView() const {return m_indexView;} + + inline const uint64_t getIndexCount() const + { + return m_indexView.getElementCount(); + } + + protected: + virtual inline ~IIndexableGeometry() = default; + + // Needs to be hidden because ICPU base class shall check mutability + inline bool setIndexView(SDataView&& view) + { + if (!view || view.composed.isFormattedScalarInteger()) + { + m_indexView = std::move(view); + return true; + } + return false; + } + + // + SDataView m_indexView = {}; +}; + +} + +// +namespace nbl::core +{ +template +struct blake3_hasher::update_impl +{ + static inline void __call(blake3_hasher& hasher, const asset::IGeometryBase::SDataViewBase& input) + { + hasher << input.stride; + hasher << input.format; + hasher << input.rangeFormat; + input.visitAABB([&hasher](auto& aabb)->void{hasher.update(&aabb,sizeof(aabb));}); + } +}; +} +#endif \ No newline at end of file diff --git a/include/nbl/asset/IGeometryCollection.h b/include/nbl/asset/IGeometryCollection.h new file mode 100644 index 0000000000..cd72990365 --- /dev/null +++ b/include/nbl/asset/IGeometryCollection.h @@ -0,0 +1,129 @@ +// Copyright (C) 2025-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_I_GEOMETRY_COLLECTION_H_INCLUDED_ +#define _NBL_ASSET_I_GEOMETRY_COLLECTION_H_INCLUDED_ + + +#include "nbl/asset/IPolygonGeometry.h" + + +namespace nbl::asset +{ +// Collection of geometries of the same type (but e.g. with different formats or transforms) +template +class NBL_API2 IGeometryCollection : public virtual core::IReferenceCounted +{ + public: + using SDataView = IGeometry::SDataView; + + // + inline const auto& getAABB() const {return m_aabb;} + + // + struct SGeometryReference + { + inline operator bool() const + { + if (!geometry) + return false; + if (jointRedirectView.src) + { + if (!jointRedirectView.isFormattedScalarInteger()) + return false; + if (jointRedirectView.getElementCount()getJointCount()*2) + return false; + } + else + return true; + } + + inline bool hasTransform() const {return !core::isnan(transform[0][0]);} + + hlsl::float32_t3x4 transform = hlsl::float32_t3x4(std::numeric_limits::quiet_NaN()); + core::smart_refctd_ptr> geometry = {}; + // The geometry may be using a smaller set of joint/bone IDs which need to be remapped to a larger or common skeleton + // Ignored if this geometry collection is not skinned or the `geometry` doesn't have a weight view. + // If not provided, its treated as-if an iota {0,1,2,...} view was provided + SDataView jointRedirectView = {}; + }; + inline const core::vector& getGeometries() const {return m_geometries;} + + // This is for the whole geometry collection, geometries remap to those. + // Each element is a row of an affine transformation matrix + inline uint32_t getJointCount() const {return m_inverseBindPoseView.getElementCount()/3;} + + // + inline bool isSkinned() const {return getJointCount()>0;} + // View of matrices being the inverse bind pose + inline const SDataView& getInverseBindPoseView() const {return m_inverseBindPoseView;} + + + protected: + virtual ~IGeometryCollection() = default; + + // + inline core::vector& getGeometries() {return m_geometries;} + + // returns whether skinning was enabled or disabled + inline bool setSkin(SDataView&& inverseBindPoseView, SDataView&& jointAABBView) + { + // disable skinning + m_inverseBindPoseView = {}; + m_jointAABBView = {}; + // need a format with one row per element + const auto ibpFormat = inverseBindPoseView.composed.format; + if (!inverseBindPoseView || !inverseBindPoseView.composed.isFormatted() || getFormatChannelCount(ibpFormat)!=4) + return false; + const auto matrixRowCount = inverseBindPoseView.getElementCount(); + // and 3 elements per matrix + if (matrixRowCount==0 || (matrixRowCount%3)!=0) + return false; + const auto jointCount = matrixRowCount/3; + // ok now check the AABB stuff + if (jointAABBView) + { + // needs to be formatted + if (!jointAABBView.composed.isFormatted()) + return false; + // each element is a AABB vertex, so need 2 per joint + if (jointAABBView.getElementCount()!=jointCount*2) + return false; + } + m_inverseBindPoseView = std::move(inverseBindPoseView); + m_jointAABBView = std::move(jointAABBView); + return true; + } + + // need to be protected because of the mess around `transform` requires us to provide diffferent signatures for ICPUGeometryCollection and IGPUGeometryCollection + using BLASTriangles = IBottomLevelAccelerationStructure::Triangles>; + template// requires std::is_same_v()),decltype(BLASTriangles&)> + inline Iterator exportForBLAS(Iterator out, Callback& setTransform) const + { + for (const auto& ref : m_geometries) + { + // not a polygon geometry + const auto* geo = ref.geometry.get(); + if (geo->getPrimitiveType()==IGeometryBase::EPrimitiveType::Polygon) + continue; + const auto* polyGeo = static_cast*>(geo); + *out = polyGeo->exportForBLAS(); + if (out->vertexData[0]) + out++; + } + return out; + } + + + // For the entire collection, as always it should NOT include any geometry which is affected by a joint. + IGeometryBase::SAABBStorage m_aabb; + SDataView m_inverseBindPoseView = {}; + // The AABBs gathered from all geometries (optional) and are in "bone-space" so there's no need for OBB option, + // joint influence is usually aligned to the covariance matrix of geometry affected by it. + SDataView m_jointAABBView = {}; + // + core::vector m_geometries; +}; + +} +#endif \ No newline at end of file diff --git a/include/nbl/asset/IMesh.h b/include/nbl/asset/IMesh.h deleted file mode 100644 index 8f1eb0b8f2..0000000000 --- a/include/nbl/asset/IMesh.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_I_MESH_H_INCLUDED__ -#define __NBL_ASSET_I_MESH_H_INCLUDED__ - -#include "nbl/core/IReferenceCounted.h" -#include "aabbox3d.h" - -namespace nbl -{ -namespace asset -{ - //! Class which holds the geometry of an object. - /** An IMesh is nothing more than a collection of some mesh buffers - (IMeshBuffer). - */ - template - class IMesh : public virtual core::IReferenceCounted - { - protected: - //! The cached bounding box of this mesh - core::aabbox3d m_cachedBoundingBox; - - virtual ~IMesh() {} - - public: - //! Get iterator range of the attached meshbuffers - virtual core::SRange getMeshBuffers() const = 0; - virtual core::SRange getMeshBuffers() = 0; - - //! Get an axis aligned bounding box of the mesh. - /** \return Bounding box of this mesh. */ - inline const core::aabbox3df& getBoundingBox() const { return m_cachedBoundingBox; } - - //! - inline virtual void setBoundingBox(const core::aabbox3df& newBoundingBox) - { - m_cachedBoundingBox = newBoundingBox; - } - }; - -} // end namespace asset -} // end namespace nbl - -#endif - diff --git a/include/nbl/asset/IMeshBuffer.h b/include/nbl/asset/IMeshBuffer.h deleted file mode 100644 index a4f1b895dc..0000000000 --- a/include/nbl/asset/IMeshBuffer.h +++ /dev/null @@ -1,324 +0,0 @@ -// Copyright (C) 2018-2021 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h -#ifndef _NBL_ASSET_I_MESH_BUFFER_H_INCLUDED_ -#define _NBL_ASSET_I_MESH_BUFFER_H_INCLUDED_ - - -#include "nbl/asset/IRenderpassIndependentPipeline.h" -#include "nbl/asset/ECommonEnums.h" - -#include - -namespace nbl::asset -{ - -template -class IMeshBuffer : public virtual core::IReferenceCounted -{ - public: - _NBL_STATIC_INLINE_CONSTEXPR size_t MAX_PUSH_CONSTANT_BYTESIZE = 128u; - - _NBL_STATIC_INLINE_CONSTEXPR size_t MAX_VERTEX_ATTRIB_COUNT = SVertexInputParams::MAX_VERTEX_ATTRIB_COUNT; - _NBL_STATIC_INLINE_CONSTEXPR size_t MAX_ATTR_BUF_BINDING_COUNT = SVertexInputParams::MAX_ATTR_BUF_BINDING_COUNT; - - protected: - virtual ~IMeshBuffer() = default; - - alignas(32) core::aabbox3df boundingBox; - - // TODO: format, stride, input rate - SBufferBinding m_vertexBufferBindings[MAX_ATTR_BUF_BINDING_COUNT]; - SBufferBinding m_indexBufferBinding; - - //! Skin - SBufferBinding m_inverseBindPoseBufferBinding,m_jointAABBBufferBinding; - - //! Descriptor set which goes to set=3 - core::smart_refctd_ptr m_descriptorSet; - - alignas(64) uint8_t m_pushConstantsData[MAX_PUSH_CONSTANT_BYTESIZE]{};//by putting m_pushConstantsData here, alignas(64) takes no extra place - - // TODO: remove descriptor set, pipeline & instancing info - //! Pipeline for drawing - core::smart_refctd_ptr m_pipeline; - - // draw params - uint32_t indexCount = 0u; - uint32_t instanceCount = 1u; - int32_t baseVertex = 0; - uint32_t baseInstance = 0u; - - // others - uint32_t jointCount : 11; - uint32_t maxJointsPerVx : 3; - uint32_t indexType : 2; - - public: - IMeshBuffer() : jointCount(0u), maxJointsPerVx(0u), indexType(EIT_UNKNOWN) {} - //! Constructor. - IMeshBuffer(core::smart_refctd_ptr&& _pipeline, - core::smart_refctd_ptr&& _ds, - SBufferBinding _vtxBindings[MAX_ATTR_BUF_BINDING_COUNT], - SBufferBinding&& _indexBinding - ) : boundingBox(), m_indexBufferBinding(std::move(_indexBinding)), - m_inverseBindPoseBufferBinding(), m_jointAABBBufferBinding(), - m_descriptorSet(std::move(_ds)), m_pipeline(std::move(_pipeline)), - indexCount(0u), instanceCount(1u), baseVertex(0), baseInstance(0u), - jointCount(0u), maxJointsPerVx(0u), indexType(EIT_UNKNOWN) - { - if (_vtxBindings) - std::copy(_vtxBindings, _vtxBindings+MAX_ATTR_BUF_BINDING_COUNT, m_vertexBufferBindings); - } - - inline bool isAttributeEnabled(uint32_t attrId) const - { - if (attrId >= MAX_VERTEX_ATTRIB_COUNT) - return false; - if (!m_pipeline) - return false; - - const auto& vtxInputParams = m_pipeline->getCachedCreationParams().vertexInput; - if (!(vtxInputParams.enabledAttribFlags & (1u<= MAX_ATTR_BUF_BINDING_COUNT) - return false; - if (!m_pipeline) - return false; - - const auto& vtxInputParams = m_pipeline->getCachedCreationParams().vertexInput; - if (!(vtxInputParams.enabledBindingFlags & (1u<getCachedCreationParams().vertexInput; - return vtxInputParams.attributes[attrId].binding; - } - inline E_FORMAT getAttribFormat(uint32_t attrId) const - { - const auto& vtxInputParams = m_pipeline->getCachedCreationParams().vertexInput; - return static_cast(vtxInputParams.attributes[attrId].format); - } - inline uint32_t getAttribStride(uint32_t attrId) const - { - const auto& vtxInputParams = m_pipeline->getCachedCreationParams().vertexInput; - const uint32_t bnd = getBindingNumForAttribute(attrId); - return vtxInputParams.bindings[bnd].stride; - } - inline uint32_t getAttribOffset(uint32_t attrId) const - { - const auto& vtxInputParams = m_pipeline->getCachedCreationParams().vertexInput; - return vtxInputParams.attributes[attrId].relativeOffset; - } - - inline SBufferBinding& getAttribBoundBuffer(uint32_t attrId) - { - const uint32_t bnd = getBindingNumForAttribute(attrId); - return m_vertexBufferBindings[bnd]; - } - inline const SBufferBinding& getAttribBoundBuffer(uint32_t attrId) const - { - const uint32_t bnd = getBindingNumForAttribute(attrId); - return reinterpret_cast&>(m_vertexBufferBindings[bnd]); - } - - inline const SBufferBinding* getVertexBufferBindings() - { - return m_vertexBufferBindings; - } - inline const SBufferBinding* getVertexBufferBindings() const - { - return reinterpret_cast*>(m_vertexBufferBindings); - } - - inline const SBufferBinding& getIndexBufferBinding() - { - return m_indexBufferBinding; - } - inline const SBufferBinding& getIndexBufferBinding() const - { - return reinterpret_cast&>(m_indexBufferBinding); - } - - inline const SBufferBinding& getInverseBindPoseBufferBinding() - { - return m_inverseBindPoseBufferBinding; - } - inline const SBufferBinding& getInverseBindPoseBufferBinding() const - { - return reinterpret_cast&>(m_inverseBindPoseBufferBinding); - } - - inline const SBufferBinding& getJointAABBBufferBinding() - { - return m_jointAABBBufferBinding; - } - inline const SBufferBinding& getJointAABBBufferBinding() const - { - return reinterpret_cast&>(m_jointAABBBufferBinding); - } - - virtual inline bool setVertexBufferBinding(SBufferBinding&& bufferBinding, uint32_t bindingIndex) - { - if (bindingIndex >= MAX_ATTR_BUF_BINDING_COUNT) - return false; - - m_vertexBufferBindings[bindingIndex] = std::move(bufferBinding); - - return true; - } - - virtual inline void setIndexBufferBinding(SBufferBinding&& bufferBinding) - { - // assert(isMutable()); - - m_indexBufferBinding = std::move(bufferBinding); - } - - virtual inline void setAttachedDescriptorSet(core::smart_refctd_ptr&& descriptorSet) - { - //assert(isMutable()); - m_descriptorSet = std::move(descriptorSet); - } - - virtual inline void setPipeline(core::smart_refctd_ptr&& pipeline) - { - //assert(isMutable()); - m_pipeline = std::move(pipeline); - } - - inline uint64_t getAttribCombinedOffset(uint32_t attrId) const - { - const auto& buf = getAttribBoundBuffer(attrId); - return buf.offset+static_cast(getAttribOffset(attrId)); - } - - //! Returns bound on JointID in the vertex attribute - inline auto getJointCount() const { return jointCount; } - - //! Returns max joint influences - inline auto getMaxJointsPerVertex() const { return maxJointsPerVx; } - - //! - virtual inline bool isSkinned() const - { - return jointCount>0u && maxJointsPerVx>0u && m_inverseBindPoseBufferBinding.buffer && - m_inverseBindPoseBufferBinding.offset+jointCount*sizeof(core::matrix3x4SIMD)<=m_inverseBindPoseBufferBinding.buffer->getSize(); - } - - //! - virtual inline bool setSkin( - SBufferBinding&& _inverseBindPoseBufferBinding, - SBufferBinding&& _jointAABBBufferBinding, - const uint32_t _jointCount, const uint32_t _maxJointsPerVx - ) - { - if (!_inverseBindPoseBufferBinding.buffer || !_jointAABBBufferBinding.buffer || _jointCount==0u) - return false; - - // a very arbitrary constraint - if (_maxJointsPerVx==0u || _maxJointsPerVx>4u) - return false; - - if (_inverseBindPoseBufferBinding.offset+_jointCount*sizeof(core::matrix3x4SIMD)>_inverseBindPoseBufferBinding.buffer->getSize()) - return false; - - m_inverseBindPoseBufferBinding = std::move(_inverseBindPoseBufferBinding); - m_jointAABBBufferBinding = std::move(_jointAABBBufferBinding); - jointCount = _jointCount; - maxJointsPerVx = _maxJointsPerVx; - return true; - } - - //! - inline const DescSetType* getAttachedDescriptorSet() const - { - return m_descriptorSet.get(); - } - - //! - inline const PipelineType* getPipeline() const - { - return m_pipeline.get(); - } - - //! Get type of index data which is stored in this meshbuffer. - /** \return Index type of this buffer. */ - inline E_INDEX_TYPE getIndexType() const {return static_cast(indexType);} - inline void setIndexType(const E_INDEX_TYPE type) - { - indexType = type; - } - - //! Get amount of indices in this meshbuffer. - /** \return Number of indices in this buffer. */ - inline auto getIndexCount() const {return indexCount;} - //! It sets amount of indices - value that is being passed to glDrawArrays as vertices amount or to glDrawElements as index amount. - /** @returns Whether set amount exceeds mapped buffer's size. Regardless of result the amount is set. */ - inline bool setIndexCount(const uint32_t newIndexCount) - { - indexCount = newIndexCount; - if (m_indexBufferBinding.buffer) - { - switch (indexType) - { - case EIT_16BIT: - return indexCount*sizeof(uint16_t)+m_indexBufferBinding.offset < m_indexBufferBinding.buffer->getSize(); - case EIT_32BIT: - return indexCount*sizeof(uint32_t)+m_indexBufferBinding.offset < m_indexBufferBinding.buffer->getSize(); - default: - return false; - } - } - - return true; - } - - //! Accesses base vertex number. - /** @returns base vertex number. */ - inline int32_t getBaseVertex() const {return baseVertex;} - //! Sets base vertex. - inline void setBaseVertex(const int32_t baseVx) - { - baseVertex = baseVx; - } - - inline uint32_t getInstanceCount() const {return instanceCount;} - inline void setInstanceCount(const uint32_t count) - { - instanceCount = count; - } - - inline uint32_t getBaseInstance() const {return baseInstance;} - inline void setBaseInstance(const uint32_t base) - { - baseInstance = base; - } - - - //! Get the axis aligned bounding box of this meshbuffer. - /** \return Axis aligned bounding box of this buffer. */ - inline const core::aabbox3df& getBoundingBox() const {return boundingBox;} - - //! Set axis aligned bounding box - /** \param box User defined axis aligned bounding box to use - for this buffer. */ - inline virtual void setBoundingBox(const core::aabbox3df& box) - { - boundingBox = box; - } - - uint8_t* getPushConstantsDataPtr() { return m_pushConstantsData; } - const uint8_t* getPushConstantsDataPtr() const { return m_pushConstantsData; } -}; - -} - -#endif \ No newline at end of file diff --git a/include/nbl/asset/IMorphTargets.h b/include/nbl/asset/IMorphTargets.h new file mode 100644 index 0000000000..14265aa71a --- /dev/null +++ b/include/nbl/asset/IMorphTargets.h @@ -0,0 +1,83 @@ +// Copyright (C) 2025-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_I_MORPH_TARGETS_H_INCLUDED_ +#define _NBL_ASSET_I_MORPH_TARGETS_H_INCLUDED_ + + +#include "nbl/asset/IGeometryCollection.h" + + +namespace nbl::asset +{ +// Unlike glTF we don't require same index buffers and maintaining isomorphisms between primitives/vertices in different geometries. But most of the use cases would have such mappings. +// The semantics are really up to you, these are just collections of Geometries which can be swapped out or interpolated between. +// Note: A LoD set can also be viewed as a morph shape per LoD, while a Motion Blur BLAS can be viewed as representing the interval between two Morph Targets. +template +class NBL_API2 IMorphTargets : public virtual core::IReferenceCounted +{ + public: + struct index_t + { + explicit inline index_t(uint32_t _value) : value(_value) {} + + inline operator bool() const {return value!=(~0u);} + + uint32_t value = ~0u; + }; + + inline uint32_t getTargetCount() const + { + return static_cast(m_targets.size()); + } + + template requires std::is_floating_point_v + struct SInterpolants + { + index_t indices[Degree]; + Scalar weights[Degree-1]; + }; + + template requires std::is_floating_point_v + inline SInterpolants getLinearBlend(const Scalar blend) const + { + SInterpolants retval; + if (!m_targets.empty()) + { + const Scalar maxMorph = getTargetCount(); + retval.indices[0] = index_t(hlsl::clamp(blend,Scalar(0),maxMorph)); + retval.indices[1] = index_t(hlsl::min(retval.indices[0].value+Scalar(1),maxMorph)); + retval.weights[0] = blend-Scalar(retval.indices[0].value); + } + return retval; + } + + struct STarget + { + inline operator bool() const + { + return geoCollection && (!jointRedirectView || jointRedirectView.composed.isFormattedScalarInteger()); + } + + core::smart_refctd_ptr geoCollection = {}; + // The geometry may be using a smaller set of joint/bone IDs which need to be remapped to a larger or common skeleton. + // Ignored if the collection is not skinned. + GeometryCollection::SDataView jointRedirectView = {}; + }; + inline const core::vector& getTargets() const {return m_targets;} + + protected: + virtual ~IMorphTargets() = default; + + // + inline core::vector& getTargets() {return m_targets;} + + // TODO: utility to make motion-blur IBottomLevelAccelerationStructure::Triangles from two targets + + // + core::vector m_targets; + // no point keeping an overall AABB, because the only reason to do that is to skip animation/indexing logic all together +}; +} + +#endif \ No newline at end of file diff --git a/include/nbl/asset/IPolygonGeometry.h b/include/nbl/asset/IPolygonGeometry.h new file mode 100644 index 0000000000..4d021c178c --- /dev/null +++ b/include/nbl/asset/IPolygonGeometry.h @@ -0,0 +1,274 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_I_POLYGON_GEOMETRY_H_INCLUDED_ +#define _NBL_ASSET_I_POLYGON_GEOMETRY_H_INCLUDED_ + + +#include "nbl/asset/IGeometry.h" +#include "nbl/asset/RasterizationStates.h" +#include "nbl/asset/IAccelerationStructure.h" + +#include + +namespace nbl::asset +{ +// +class IPolygonGeometryBase : public virtual core::IReferenceCounted +{ + public: + // + class IIndexingCallback + { + public: + // how many vertices per polygon + inline uint8_t degree() const + { + const auto retval = degree_impl(); + assert(retval>0); + return retval; + } + // at which we consume indices for each new polygon + inline uint8_t rate() const + { + const auto retval = rate_impl(); + assert(retval>0 && retval<=degree()); + return retval; + } + + template requires (sizeof(OutT)<8 && hlsl::concepts::UnsignedIntegralScalar) + struct SContext final + { + // `indexOfIndex` is somewhat of a baseIndex + template + inline void streamOut(const uint32_t indexOfIndex, const Range& permutation) + { + auto& typedOut = reinterpret_cast(out); + if (indexBuffer) + switch (indexSize) + { + case 1: + for (const auto relIx : permutation) + *(typedOut++) = reinterpret_cast(indexBuffer)[indexOfIndex+relIx]; + break; + case 2: + for (const auto relIx : permutation) + *(typedOut++) = reinterpret_cast(indexBuffer)[indexOfIndex+relIx]; + break; + case 4: + for (const auto relIx : permutation) + *(typedOut++) = reinterpret_cast(indexBuffer)[indexOfIndex+relIx]; + break; + default: + assert(false); + break; + } + else + for (const auto relIx : permutation) + *(typedOut++) = indexOfIndex+relIx; + } + + // always the base pointer, doesn't get advanced + const void* const indexBuffer; + const uint64_t indexSize : 3; + const uint64_t beginPrimitive : 30; + const uint64_t endPrimitive : 31; + void* out; + }; + // could have been a static if not virtual + virtual void operator()(SContext& ctx) const = 0; + virtual void operator()(SContext& ctx) const = 0; + virtual void operator()(SContext& ctx) const = 0; + + // default is unknown + virtual inline E_PRIMITIVE_TOPOLOGY knownTopology() const {return static_cast(~0);} + + protected: + virtual uint8_t degree_impl() const = 0; + virtual uint8_t rate_impl() const = 0; + }; + // + NBL_API2 static IIndexingCallback* PointList(); + NBL_API2 static IIndexingCallback* LineList(); + NBL_API2 static IIndexingCallback* TriangleList(); + NBL_API2 static IIndexingCallback* QuadList(); + // TODO: Adjacency, Patch, etc. + NBL_API2 static IIndexingCallback* TriangleStrip(); + NBL_API2 static IIndexingCallback* TriangleFan(); + + // This should be a pointer to a stateless singleton (think of it more like a dynamic enum/template than anything else) + inline const IIndexingCallback* getIndexingCallback() const {return m_indexing;} + + protected: + virtual inline ~IPolygonGeometryBase() = default; + + // indexing callback cannot be cleared + inline bool setIndexingCallback(IIndexingCallback* indexing) + { + if (!indexing) + return false; + const auto deg = m_indexing->degree(); + if (deg==0 || m_indexing->rate()==0 || m_indexing->rate()>deg) + return false; + m_indexing = indexing; + return true; + } + + // + const IIndexingCallback* m_indexing = nullptr; +}; + +// Don't want to overengineer, support for variable vertex count (order) polgyon meshes is not encouraged or planned. +// If you want different polygon types in same model, bucket the polgyons into separate geometries and reunite in a single collection. +template +class IPolygonGeometry : public IIndexableGeometry, public IPolygonGeometryBase +{ + using base_t = IIndexableGeometry; + + protected: + using EPrimitiveType = base_t::EPrimitiveType; + using SDataView = base_t::SDataView; + using BLASTriangles = IBottomLevelAccelerationStructure::Triangles>; + + public: + // + virtual inline bool valid() const override + { + if (!base_t::valid()) + return false; + if (!m_indexing) + return false; + // there needs to be at least one vertex to reference (it also needs to be formatted) + const auto& positionBase = base_t::m_positionView.composed; + const auto vertexCount = base_t::m_positionView.getElementCount(); + if (vertexCount==0 || !positionBase.isFormatted()) + return false; + if (m_normalView && m_normalView.getElementCount()degree(); + if (vertexReferenceCountrate()+1; + } + + // For when the geometric normal of the patch isn't enough and you want interpolated custom normals + inline const SDataView& getNormalView() const {return m_normalView;} + + // Its also a Max Joint ID that `m_jointWeightViews` can reference + inline uint32_t getJointCount() const override final + { + return m_jointCount; + } + + // SoA instead of AoS, first component is the first bone influece, etc. + struct SJointWeight + { + // one thing this doesn't check is whether every vertex has a weight and index + inline operator bool() const {return indices && isIntegerFormat(indices.composed.format) && weights && weights.composed.isFormatted() && indices.getElementCount()==weights.getElementCount();} + + SDataView indices; + // Assumption is that only non-zero weights are present, which is why the joints are indexed (sparseness) + // Zero weights are acceptable but only if they form a contiguous block including the very last component of the very last weight view + SDataView weights; + }; + // It's a vector in case you need more than 4 influences per bone + inline const core::vector& getJointWeightViews() const {return m_jointWeightViews;} + + // For User defined semantics + inline const core::vector& getAuxAttributeViews() const {return m_auxAttributeViews;} + + + // Does not set the `transform` or `geometryFlags` fields, because it doesn't care about it. + // Also won't set second set of vertex data, opacity mipmaps, etc. + inline BLASTriangles exportForBLAS() const + { + BLASTriangles retval = {}; + // must be a triangle list, but don't want to compare pointers + if (m_indexing && m_indexing->knownTopology()==EPT_TRIANGLE_LIST)// && m_indexing->degree() == TriangleList()->degree() && m_indexing->rate() == TriangleList->rate()) + { + auto indexType = EIT_UNKNOWN; + // disallowed index format + if (base_t::m_indexView) + { + switch (base_t::m_indexView.composed.format) + { + case EF_R16_UINT: + indexType = EIT_16BIT; + break; + case EF_R32_UINT: [[fallthrough]]; + indexType = EIT_32BIT; + break; + default: + break; + } + if (indexType==EIT_UNKNOWN) + return retval; + } + retval.vertexData[0] = base_t::m_positionView.src; + retval.indexData = base_t::m_indexView.src; + retval.maxVertex = base_t::m_positionView.getElementCount() - 1; + retval.vertexStride = base_t::m_positionView.composed.getStride(); + retval.vertexFormat = base_t::m_positionView.composed.format; + retval.indexType = indexType; + } + return retval; + } + + protected: + virtual ~IPolygonGeometry() = default; + + // 64bit indices are just too much to deal with in all the other code + // Also if you have more than 2G vertex references in a single geometry there's something wrong with your architecture + // Note that this still allows 6GB vertex attribute streams (assuming at least 3 bytes for a postion) + inline bool setIndexView(SDataView&& view) + { + if (view) + { + const auto format = view.composed.format; + if (!view.composed.isFormattedScalarInteger() || format == EF_R64_UINT || format == EF_R64_SINT) + return false; + if (view.getElementCount()>(1u<<31)) + return false; + } + base_t::m_indexView = std::move(view); + return true; + } + + // + core::vector m_jointWeightViews = {}; + // + core::vector m_auxAttributeViews = {}; + // + SDataView m_normalView = {}; + // + uint32_t m_jointCount = 0; +}; + +} +#endif \ No newline at end of file diff --git a/include/nbl/asset/IRenderpassIndependentPipeline.h b/include/nbl/asset/IRenderpassIndependentPipeline.h deleted file mode 100644 index feeaff7c99..0000000000 --- a/include/nbl/asset/IRenderpassIndependentPipeline.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (C) 2018-2023 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h -#ifndef _NBL_ASSET_I_RENDERPASS_INDEPENDENT_PIPELINE_H_INCLUDED_ -#define _NBL_ASSET_I_RENDERPASS_INDEPENDENT_PIPELINE_H_INCLUDED_ - - -#include "nbl/macros.h" - -#include "nbl/core/declarations.h" - -#include "nbl/asset/IGraphicsPipeline.h" - -#include - - -namespace nbl::asset -{ - -//! Deprecated class but needs to stay around till Material Compiler 2 -class IRenderpassIndependentPipeline -{ - public: - struct SCachedCreationParams final - { - SVertexInputParams vertexInput = {}; - SPrimitiveAssemblyParams primitiveAssembly = {}; - SRasterizationParams rasterization = {}; - SBlendParams blend = {}; - }; - - inline const SCachedCreationParams& getCachedCreationParams() const {return m_cachedParams;} - - constexpr static inline size_t GRAPHICS_SHADER_STAGE_COUNT = 5u; - - protected: - IRenderpassIndependentPipeline(const SCachedCreationParams& _cachedParams) : m_cachedParams(_cachedParams) {} - virtual ~IRenderpassIndependentPipeline() = default; - - SCachedCreationParams m_cachedParams; -}; - -} -#endif \ No newline at end of file diff --git a/include/nbl/asset/asset.h b/include/nbl/asset/asset.h index 84d9b9ccd2..80c5374806 100644 --- a/include/nbl/asset/asset.h +++ b/include/nbl/asset/asset.h @@ -10,18 +10,14 @@ #include "nbl/system/declarations.h" #include "nbl/system/definitions.h" // TODO: split `asset.h` into decl and def -// utils -#include "nbl/asset/asset_utils.h" - // format #include "nbl/asset/format/EFormat.h" #include "nbl/asset/format/convertColor.h" #include "nbl/asset/format/decodePixels.h" #include "nbl/asset/format/encodePixels.h" -// base +// buffers #include "nbl/asset/ICPUBuffer.h" -#include "nbl/asset/IMesh.h" //depr // images #include "nbl/asset/ICPUImage.h" @@ -51,31 +47,25 @@ #include "nbl/asset/ICPUAnimationLibrary.h" #include "nbl/asset/ICPUSkeleton.h" -// meshes -#include "nbl/asset/ICPUMeshBuffer.h" -#include "nbl/asset/ICPUMesh.h" -#include "nbl/asset/utils/IGeometryCreator.h" -// #include "nbl/asset/utils/IMeshPacker.h" +// geometry +#include "nbl/asset/utils/CGeometryCreator.h" +#include "nbl/asset/ICPUGeometryCollection.h" +#include "nbl/asset/ICPUMorphTargets.h" // manipulation + reflection + introspection -#include "nbl/asset/utils/IMeshManipulator.h" +#include "nbl/asset/utils/CSmoothNormalGenerator.h" #include "nbl/asset/IAssetManager.h" // importexport #include "nbl/asset/interchange/IAssetLoader.h" #include "nbl/asset/interchange/IImageLoader.h" -#include "nbl/asset/interchange/IRenderpassIndependentPipelineLoader.h" +#include "nbl/asset/interchange/IGeometryLoader.h" #include "nbl/asset/interchange/IAssetWriter.h" #include "nbl/asset/interchange/IImageWriter.h" #include "nbl/asset/metadata/COpenEXRMetadata.h" #include "nbl/asset/metadata/CMTLMetadata.h" -#include "nbl/asset/metadata/COBJMetadata.h" #include "nbl/asset/metadata/CPLYMetadata.h" #include "nbl/asset/metadata/CSTLMetadata.h" -//VT -// #include "nbl/asset/utils/CCPUMeshPackerV1.h" -// #include "nbl/asset/utils/CCPUMeshPackerV2.h" - #endif diff --git a/include/nbl/asset/asset_utils.h b/include/nbl/asset/asset_utils.h deleted file mode 100644 index 8e4e35a733..0000000000 --- a/include/nbl/asset/asset_utils.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_UTILS_H_INCLUDED__ -#define __NBL_ASSET_UTILS_H_INCLUDED__ - -// TODO: this whole header should die - -#include "nbl/asset/ICPUBuffer.h" -#include - -namespace nbl { -namespace asset -{ - -inline void fillBufferWithDWORD(ICPUBuffer* _buf, uint32_t _val) -{ - const size_t dwCnt = _buf->getSize()/4ull; - const size_t rem = _buf->getSize()-(dwCnt*4ull); - - uint32_t* dwptr = reinterpret_cast(_buf->getPointer()); - std::fill(dwptr, dwptr+dwCnt, _val); - memcpy(dwptr+dwCnt, &_val, rem); -} - -inline void fillBufferWithDeadBeef(ICPUBuffer* _buf) -{ - fillBufferWithDWORD(_buf, 0xdeadbeefu); -} - -#include "nbl/nblpack.h" -//! Designed for use with interface blocks declared with `layout (row_major, std140)` -// TODO: change members to core::matrix3x4SIMD and core::matrix4SIMD -struct SBasicViewParameters -{ - float MVP[4*4]; - //! Might be used for Model matrix just as well - float MV[3*4]; - //! 3x3 but each row is padded to 4 floats (16 bytes), so that actually we have 3x4 - //! so we have 3 floats free for use (last element of each row) which can be used for storing some vec3 (most likely camera position) - //! This vec3 is accessible in GLSL by accessing 4th column with [] operator - float NormalMat[3*3+3]; -} PACK_STRUCT; -#include "nbl/nblunpack.h" - -static_assert(sizeof(SBasicViewParameters) == sizeof(float) * (4 * 4 + 2 * 3 * 4)); - -}} - -#endif \ No newline at end of file diff --git a/include/nbl/asset/format/decodePixels.h b/include/nbl/asset/format/decodePixels.h index bbe93c40b9..dd887a08f5 100644 --- a/include/nbl/asset/format/decodePixels.h +++ b/include/nbl/asset/format/decodePixels.h @@ -1680,7 +1680,6 @@ namespace asset case asset::EF_G8_B8R8_2PLANE_422_UNORM: decodePixels(_pix, _output, _blockX, _blockY); return true; case asset::EF_G8_B8_R8_3PLANE_444_UNORM: decodePixels(_pix, _output, _blockX, _blockY); return true; default: - assert(!"Format decode not supported"); return false; } } diff --git a/include/nbl/asset/interchange/IAssetLoader.h b/include/nbl/asset/interchange/IAssetLoader.h index 5f85cf2c9d..38480b429e 100644 --- a/include/nbl/asset/interchange/IAssetLoader.h +++ b/include/nbl/asset/interchange/IAssetLoader.h @@ -14,7 +14,7 @@ namespace nbl::asset { -class IMeshManipulator; +class CPolygonGeometryManipulator; //! A class automating process of loading Assets from resources, eg. files /** @@ -117,7 +117,7 @@ class NBL_API2 IAssetLoader : public virtual core::IReferenceCounted const uint8_t* decryptionKey; E_CACHING_FLAGS cacheFlags; E_LOADER_PARAMETER_FLAGS loaderFlags; //!< Flags having an impact on extraordinary tasks during loading process - IMeshManipulator* meshManipulatorOverride = nullptr; //!< pointer used for specifying custom mesh manipulator to use, if nullptr - default mesh manipulator will be used + CPolygonGeometryManipulator* meshManipulatorOverride = nullptr; //!< pointer used for specifying custom mesh manipulator to use, if nullptr - default mesh manipulator will be used std::filesystem::path workingDirectory = ""; system::logger_opt_ptr logger; }; diff --git a/include/nbl/asset/interchange/IAssetWriter.h b/include/nbl/asset/interchange/IAssetWriter.h index 8c41f3f327..694053df5e 100644 --- a/include/nbl/asset/interchange/IAssetWriter.h +++ b/include/nbl/asset/interchange/IAssetWriter.h @@ -1,18 +1,17 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_I_ASSET_WRITER_H_INCLUDED_ +#define _NBL_ASSET_I_ASSET_WRITER_H_INCLUDED_ -#ifndef __NBL_ASSET_I_ASSET_WRITER_H_INCLUDED__ -#define __NBL_ASSET_I_ASSET_WRITER_H_INCLUDED__ #include "nbl/system/IFile.h" #include "nbl/system/ILogger.h" #include "nbl/asset/IAsset.h" -namespace nbl -{ -namespace asset + +namespace nbl::asset { //! Writing flags @@ -192,6 +191,5 @@ class IAssetWriter : public virtual core::IReferenceCounted static void getDefaultOverride(IAssetWriterOverride*& _out) { _out = &s_defaultOverride; } }; -}} //nbl::asset - +} //nbl::asset #endif \ No newline at end of file diff --git a/include/nbl/asset/interchange/IGeometryLoader.h b/include/nbl/asset/interchange/IGeometryLoader.h new file mode 100644 index 0000000000..7f71826960 --- /dev/null +++ b/include/nbl/asset/interchange/IGeometryLoader.h @@ -0,0 +1,32 @@ +// Copyright (C) 2025-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_I_GEOMETRY_LOADER_H_INCLUDED_ +#define _NBL_ASSET_I_GEOMETRY_LOADER_H_INCLUDED_ + + +#include "nbl/core/declarations.h" + +#include "nbl/asset/ICPUPolygonGeometry.h" +#include "nbl/asset/interchange/IAssetLoader.h" +#include "nbl/asset/interchange/IImageAssetHandlerBase.h" + + +namespace nbl::asset +{ + +class IGeometryLoader : public IAssetLoader +{ + public: + virtual inline uint64_t getSupportedAssetTypesBitfield() const override {return IAsset::ET_GEOMETRY;} + + protected: + IGeometryLoader() {} + virtual ~IGeometryLoader() = 0; + + private: +}; + +} + +#endif diff --git a/include/nbl/asset/interchange/IGeometryWriter.h b/include/nbl/asset/interchange/IGeometryWriter.h new file mode 100644 index 0000000000..d7e7ab964a --- /dev/null +++ b/include/nbl/asset/interchange/IGeometryWriter.h @@ -0,0 +1,31 @@ +// Copyright (C) 2025-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_I_GEOMETRY_WRITER_H_INCLUDED_ +#define _NBL_ASSET_I_GEOMETRY_WRITER_H_INCLUDED_ + + +#include "nbl/core/declarations.h" + +#include "nbl/asset/ICPUPolygonGeometry.h" +#include "nbl/asset/interchange/IAssetWriter.h" + + +namespace nbl::asset +{ + +class IGeometryWriter : public IAssetWriter +{ + public: + virtual inline uint64_t getSupportedAssetTypesBitfield() const override {return IAsset::ET_GEOMETRY;} + + protected: + IGeometryWriter() {} + virtual ~IGeometryWriter() = 0; + + private: +}; + +} + +#endif diff --git a/include/nbl/asset/interchange/IImageWriter.h b/include/nbl/asset/interchange/IImageWriter.h index 75f08febc5..8ea18095fe 100644 --- a/include/nbl/asset/interchange/IImageWriter.h +++ b/include/nbl/asset/interchange/IImageWriter.h @@ -1,9 +1,9 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_I_IMAGE_WRITER_H_INCLUDED_ +#define _NBL_ASSET_I_IMAGE_WRITER_H_INCLUDED_ -#ifndef __NBL_ASSET_I_IMAGE_WRITER_H_INCLUDED__ -#define __NBL_ASSET_I_IMAGE_WRITER_H_INCLUDED__ #include "nbl/core/declarations.h" @@ -14,9 +14,8 @@ #include "nbl/asset/filters/CFlattenRegionsImageFilter.h" -namespace nbl -{ -namespace asset + +namespace nbl::asset { class IImageWriter : public IAssetWriter, public IImageAssetHandlerBase @@ -32,6 +31,4 @@ class IImageWriter : public IAssetWriter, public IImageAssetHandlerBase }; } -} - #endif diff --git a/include/nbl/asset/interchange/IRenderpassIndependentPipelineLoader.h b/include/nbl/asset/interchange/IRenderpassIndependentPipelineLoader.h deleted file mode 100644 index 0cecb972df..0000000000 --- a/include/nbl/asset/interchange/IRenderpassIndependentPipelineLoader.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_I_RENDERPASS_INDEPENDENT_PIPELINE_LOADER_H_INCLUDED__ -#define __NBL_ASSET_I_RENDERPASS_INDEPENDENT_PIPELINE_LOADER_H_INCLUDED__ - -#include "nbl/core/declarations.h" - -#include "nbl/asset/ICPURenderpassIndependentPipeline.h" -#include "nbl/asset/interchange/IAssetLoader.h" - -namespace nbl::asset -{ - -class IRenderpassIndependentPipelineLoader : public IAssetLoader -{ - public: - virtual void initialize() override; - - protected: - IAssetManager* m_assetMgr; - core::smart_refctd_dynamic_array m_basicViewParamsSemantics; - - inline IRenderpassIndependentPipelineLoader(IAssetManager* _am) : m_assetMgr(_am) {} - virtual ~IRenderpassIndependentPipelineLoader() = 0; - - // samplers - static inline std::string genSamplerCacheKey(const asset::ICPUSampler::SParams& params) - { - // TODO: Change the HASH, ACTUALLY BUILD IT OUT OF ALL THE PARAMETERS, THERE CANNOT BE ANY COLLISIONS! - const std::size_t hash = std::hash{}(std::string_view(reinterpret_cast(¶ms), sizeof(params))); - return "nbl/builtin/sampler/" + std::to_string(hash); - } - static inline core::smart_refctd_ptr getSampler(asset::ICPUSampler::SParams&& params, const IAssetLoader::SAssetLoadContext& context, IAssetLoaderOverride* _override) - { - const auto cacheKey = genSamplerCacheKey(params); - - auto found = _override->findDefaultAsset(cacheKey,context,0u).first; // cached builtins have level 0 - if (found) - return found; - - auto sampler = core::make_smart_refctd_ptr(std::move(params)); - SAssetBundle samplerBundle = SAssetBundle(nullptr,{sampler}); - _override->insertAssetIntoCache(samplerBundle,cacheKey,context,0u); // cached builtins have level 0 - return sampler; - } - - private: -}; - -} - -#endif diff --git a/include/nbl/asset/metadata/CMTLMetadata.h b/include/nbl/asset/metadata/CMTLMetadata.h index dddc0e69d8..08279edba6 100644 --- a/include/nbl/asset/metadata/CMTLMetadata.h +++ b/include/nbl/asset/metadata/CMTLMetadata.h @@ -1,20 +1,20 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_C_MTL_METADATA_H_INCLUDED_ +#define _NBL_ASSET_C_MTL_METADATA_H_INCLUDED_ -#ifndef __NBL_ASSET_C_MTL_METADATA_H_INCLUDED__ -#define __NBL_ASSET_C_MTL_METADATA_H_INCLUDED__ #include "nbl/asset/metadata/IAssetMetadata.h" -namespace nbl -{ -namespace asset + +namespace nbl::asset { class CMTLMetadata final : public IAssetMetadata { public: +#if 0 class CRenderpassIndependentPipeline : public IRenderpassIndependentPipelineMetadata { friend class COBJMetadata; @@ -88,7 +88,6 @@ class CMTLMetadata final : public IAssetMetadata //VS Intellisense shows error here because it think vectorSIMDf is 32 bytes, but it just Intellisense - it'll build anyway static_assert(sizeof(SMaterialParameters) == 128ull, "Something went wrong"); - CRenderpassIndependentPipeline() : IRenderpassIndependentPipelineMetadata(), m_descriptorSet3(), m_materialParams(), m_name(), m_hash(0xdeadbeefu) {} CRenderpassIndependentPipeline(CRenderpassIndependentPipeline&& other) { @@ -124,45 +123,13 @@ class CMTLMetadata final : public IAssetMetadata //for permutations of pipeline representing same material but with different factors impossible to know from MTL file (like whether submesh using the material contains UVs) uint32_t m_hash; }; +#endif - CMTLMetadata(uint32_t pplnCount, core::smart_refctd_dynamic_array&& _semanticStorage) : - IAssetMetadata(), m_metaStorage(createContainer(pplnCount)), m_semanticStorage(std::move(_semanticStorage)) - { - } + CMTLMetadata() : IAssetMetadata() {} _NBL_STATIC_INLINE_CONSTEXPR const char* LoaderName = "CGraphicsPipelineLoaderMTL"; const char* getLoaderName() const override { return LoaderName; } - - //! - inline const CRenderpassIndependentPipeline* getAssetSpecificMetadata(const ICPURenderpassIndependentPipeline* asset) const - { - const auto found = IAssetMetadata::getAssetSpecificMetadata(asset); - return static_cast(found); - } - - private: - meta_container_t m_metaStorage; - core::smart_refctd_dynamic_array m_semanticStorage; - - friend class CGraphicsPipelineLoaderMTL; - inline void placeMeta( - uint32_t offset, const ICPURenderpassIndependentPipeline* ppln, - core::smart_refctd_ptr&& _descriptorSet3, - const CRenderpassIndependentPipeline::SMaterialParameters& _materialParams, - std::string&& _name, uint32_t _hash) - { - auto& meta = m_metaStorage->operator[](offset); - meta = CRenderpassIndependentPipeline( - core::SRange(m_semanticStorage->begin(),m_semanticStorage->end()), - std::move(_descriptorSet3),_materialParams, - std::move(_name),_hash - ); - - IAssetMetadata::insertAssetSpecificMetadata(ppln, &meta); - } }; } -} - #endif diff --git a/include/nbl/asset/metadata/COBJMetadata.h b/include/nbl/asset/metadata/COBJMetadata.h deleted file mode 100644 index 5eea77c473..0000000000 --- a/include/nbl/asset/metadata/COBJMetadata.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_C_OBJ_METADATA_H_INCLUDED__ -#define __NBL_ASSET_C_OBJ_METADATA_H_INCLUDED__ - -#include "nbl/asset/metadata/IAssetMetadata.h" -#include "nbl/asset/metadata/CMTLMetadata.h" - -namespace nbl -{ -namespace asset -{ - -class COBJMetadata final : public IAssetMetadata -{ - public: - using CRenderpassIndependentPipeline = typename CMTLMetadata::CRenderpassIndependentPipeline; - COBJMetadata(uint32_t pplnCount) : IAssetMetadata(), m_metaStorage(createContainer(pplnCount)) - { - } - - _NBL_STATIC_INLINE_CONSTEXPR const char* LoaderName = "CGraphicsPipelineLoaderMTL"; - const char* getLoaderName() const override { return LoaderName; } - - //! - inline const CRenderpassIndependentPipeline* getAssetSpecificMetadata(const ICPURenderpassIndependentPipeline* asset) const - { - const auto found = IAssetMetadata::getAssetSpecificMetadata(asset); - return static_cast(found); - } - - private: - meta_container_t m_metaStorage; - - friend class COBJMeshFileLoader; - inline void placeMeta(uint32_t offset, const ICPURenderpassIndependentPipeline* ppln, const CRenderpassIndependentPipeline& _meta) - { - auto& meta = m_metaStorage->operator[](offset); - meta.m_inputSemantics = _meta.m_inputSemantics; - meta.m_descriptorSet3 = _meta.m_descriptorSet3; - meta.m_materialParams = _meta.m_materialParams; - meta.m_name = _meta.m_name; - meta.m_hash = _meta.m_hash; - - IAssetMetadata::insertAssetSpecificMetadata(ppln,&meta); - } -}; - -} -} - -#endif diff --git a/include/nbl/asset/metadata/CPLYMetadata.h b/include/nbl/asset/metadata/CPLYMetadata.h index 618d21c255..13aa4ee5a8 100644 --- a/include/nbl/asset/metadata/CPLYMetadata.h +++ b/include/nbl/asset/metadata/CPLYMetadata.h @@ -1,20 +1,20 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_C_PLY_METADATA_H_INCLUDED_ +#define _NBL_ASSET_C_PLY_METADATA_H_INCLUDED_ -#ifndef __NBL_ASSET_C_PLY_METADATA_H_INCLUDED__ -#define __NBL_ASSET_C_PLY_METADATA_H_INCLUDED__ #include "nbl/asset/metadata/IAssetMetadata.h" -namespace nbl -{ -namespace asset + +namespace nbl::asset { class CPLYMetadata final : public IAssetMetadata { public: +#if 0 class CRenderpassIndependentPipeline : public IRenderpassIndependentPipelineMetadata { public: @@ -31,34 +31,12 @@ class CPLYMetadata final : public IAssetMetadata return *this; } }; - - CPLYMetadata(uint32_t pplnCount, core::smart_refctd_dynamic_array&& _semanticStorage) : - IAssetMetadata(), m_metaStorage(createContainer(pplnCount)), m_semanticStorage(std::move(_semanticStorage)) {} +#endif + CPLYMetadata() : IAssetMetadata() {} _NBL_STATIC_INLINE_CONSTEXPR const char* LoaderName = "CPLYMeshFileLoader"; const char* getLoaderName() const override { return LoaderName; } - - //! - inline const CRenderpassIndependentPipeline* getAssetSpecificMetadata(const ICPURenderpassIndependentPipeline* asset) const - { - const auto found = IAssetMetadata::getAssetSpecificMetadata(asset); - return static_cast(found); - } - - private: - meta_container_t m_metaStorage; - core::smart_refctd_dynamic_array m_semanticStorage; - - friend class CPLYMeshFileLoader; - inline void placeMeta(uint32_t offset, const ICPURenderpassIndependentPipeline* ppln) - { - auto& meta = m_metaStorage->operator[](offset); - meta.m_inputSemantics = {m_semanticStorage->begin(),m_semanticStorage->end()}; - IAssetMetadata::insertAssetSpecificMetadata(ppln,&meta); - } }; } -} - #endif \ No newline at end of file diff --git a/include/nbl/asset/metadata/CSTLMetadata.h b/include/nbl/asset/metadata/CSTLMetadata.h index 0dc9a65f69..d295b10381 100644 --- a/include/nbl/asset/metadata/CSTLMetadata.h +++ b/include/nbl/asset/metadata/CSTLMetadata.h @@ -1,20 +1,18 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_C_STL_METADATA_H_INCLUDED__ -#define __NBL_ASSET_C_STL_METADATA_H_INCLUDED__ +#ifndef _NBL_ASSET_C_STL_METADATA_H_INCLUDED_ +#define _NBL_ASSET_C_STL_METADATA_H_INCLUDED_ #include "nbl/asset/metadata/IAssetMetadata.h" -namespace nbl -{ -namespace asset +namespace nbl::asset { class CSTLMetadata final : public IAssetMetadata { public: +#if 0 class CRenderpassIndependentPipeline : public IRenderpassIndependentPipelineMetadata { public: @@ -36,37 +34,15 @@ class CSTLMetadata final : public IAssetMetadata return IRenderpassIndependentPipelineMetadata::operator!=(other); } }; - - CSTLMetadata(uint32_t pplnCount, core::smart_refctd_dynamic_array&& _semanticStorage) : - IAssetMetadata(), m_metaStorage(createContainer(pplnCount)), m_semanticStorage(std::move(_semanticStorage)) - { - } +#endif + CSTLMetadata() : IAssetMetadata() {} _NBL_STATIC_INLINE_CONSTEXPR const char* LoaderName = "CSTLMeshFileLoader"; const char* getLoaderName() const override { return LoaderName; } - //! - inline const CRenderpassIndependentPipeline* getAssetSpecificMetadata(const ICPURenderpassIndependentPipeline* asset) const - { - const auto found = IAssetMetadata::getAssetSpecificMetadata(asset); - return static_cast(found); - } - private: - meta_container_t m_metaStorage; - core::smart_refctd_dynamic_array m_semanticStorage; - friend class CSTLMeshFileLoader; - inline void placeMeta(uint32_t offset, const ICPURenderpassIndependentPipeline* ppln) - { - auto& meta = m_metaStorage->operator[](offset); - meta = CRenderpassIndependentPipeline(core::SRange(m_semanticStorage->begin(),m_semanticStorage->end())); - - IAssetMetadata::insertAssetSpecificMetadata(ppln,&meta); - } }; } -} - #endif \ No newline at end of file diff --git a/include/nbl/asset/metadata/IAssetMetadata.h b/include/nbl/asset/metadata/IAssetMetadata.h index 28a65cf8be..a6373a5b14 100644 --- a/include/nbl/asset/metadata/IAssetMetadata.h +++ b/include/nbl/asset/metadata/IAssetMetadata.h @@ -9,40 +9,27 @@ #include "nbl/asset/metadata/IImageMetadata.h" #include "nbl/asset/metadata/IImageViewMetadata.h" -#include "nbl/asset/metadata/IRenderpassIndependentPipelineMetadata.h" -#include "nbl/asset/metadata/IMeshMetadata.h" - namespace nbl::asset { -namespace impl{ - class IAssetMetadata_base : public core::IReferenceCounted{ +namespace impl +{ +class IAssetMetadata_base : public core::IReferenceCounted +{ protected: template struct asset_metadata; +}; - }; - - template<> - struct IAssetMetadata_base::asset_metadata - { - using type = IImageMetadata; - }; - template<> - struct IAssetMetadata_base::asset_metadata - { - using type = IImageViewMetadata; - }; - template<> - struct IAssetMetadata_base::asset_metadata - { - using type = IRenderpassIndependentPipelineMetadata; - }; - template<> - struct IAssetMetadata_base::asset_metadata - { - using type = IMeshMetadata; - }; - +template<> +struct IAssetMetadata_base::asset_metadata +{ + using type = IImageMetadata; +}; +template<> +struct IAssetMetadata_base::asset_metadata +{ + using type = IImageViewMetadata; +}; } @@ -83,9 +70,7 @@ class IAssetMetadata : public impl::IAssetMetadata_base std::tuple< asset_metadata_map_t, - asset_metadata_map_t, - asset_metadata_map_t, - asset_metadata_map_t + asset_metadata_map_t > m_metaMaps; diff --git a/include/nbl/asset/metadata/IMeshMetadata.h b/include/nbl/asset/metadata/IMeshMetadata.h deleted file mode 100644 index 5ce3c12980..0000000000 --- a/include/nbl/asset/metadata/IMeshMetadata.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_I_MESH_METADATA_H_INCLUDED__ -#define __NBL_ASSET_I_MESH_METADATA_H_INCLUDED__ - -#include "nbl/asset/ICPUMesh.h" - -namespace nbl -{ -namespace asset -{ - -//! -class IMeshMetadata : public core::Interface -{ - public: - struct SInstance - { - core::matrix3x4SIMD worldTform; - }; - core::SRange m_instances; - - protected: - IMeshMetadata() : m_instances(nullptr,nullptr) {} - IMeshMetadata(core::SRange&& _instances) : m_instances(std::move(_instances)) {} - virtual ~IMeshMetadata() = default; - - inline IMeshMetadata& operator=(IMeshMetadata&& other) - { - m_instances = other.m_instances; - return *this; - } -}; - -} -} - -#endif diff --git a/include/nbl/asset/metadata/IRenderpassIndependentPipelineMetadata.h b/include/nbl/asset/metadata/IRenderpassIndependentPipelineMetadata.h deleted file mode 100644 index 416c04823b..0000000000 --- a/include/nbl/asset/metadata/IRenderpassIndependentPipelineMetadata.h +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_I_RENDERPASS_INDEPENDENT_PIPELINE_METADATA_H_INCLUDED__ -#define __NBL_ASSET_I_RENDERPASS_INDEPENDENT_PIPELINE_METADATA_H_INCLUDED__ - -/* -#include "nbl/asset/ICPUDescriptorSetLayout.h" -#include "nbl/asset/ICPUSpecializedShader.h" -*/ -#include "nbl/asset/ICPUImageView.h" -#include "nbl/asset/ICPURenderpassIndependentPipeline.h" -#include "nbl/asset/IDescriptor.h" -#include "nbl/core/util/to_underlying.h" - -#include - -//#include "nbl/asset/utils/IBuiltinIncludeLoader.h" - -//#include "nbl/asset/asset_utils.h" - -namespace nbl -{ -namespace asset -{ - -//! A class to derive loader-specific pipeline metadata objects from -/** - Pipelines may sometimes require external inputs from outside of the resourced they were built with, for total flexibility - we cannot standardise "conventions" of shader inputs like in game engines or old-style frameworks. - - But we can provide useful metadata from the loader. -*/ -class IRenderpassIndependentPipelineMetadata : public core::Interface -{ - public: - //! A common struct to unify the metadata declarations. - /** - When a pipeline or meshbuffer asset require some inputs to work correctly, - they can put this info in the metadata in a custom way for every loader. - - Most meshbuffers or graphics pipelines will need to know about the model's world view projection matrix, - as well as the inverse transpose of the world matrix, and the camera world position in case of lighting. - - However more advanced loaders such as glTF may want to let us know that there is a texture being used as - an environment map, and with this knowledge we could know that we could, for example change it or provide it - to match the object with the scene. - - Remember that we always have the shader introspector which can give us the information about all the descriptors - that a shader uses, but it won't give us the semantics, or in simple english, the meaning why they are being used. - - (@see ICPURenderpassIndependentPipeilne and @see ICPUComputePipeline) - */ - struct ShaderInput - { - struct DescriptorCommon - { - uint32_t set; - uint32_t binding; - - auto operator<=>(const DescriptorCommon&) const = default; - }; - - struct CombinedImageSampler : DescriptorCommon - { - IImageView::E_TYPE viewType; - // TODO: some info about format class - - auto operator<=>(const CombinedImageSampler&) const = default; - }; - struct StorageImage : DescriptorCommon - { - E_FORMAT format; - - auto operator<=>(const StorageImage&) const = default; - }; - struct TexelBuffer : DescriptorCommon - { - // relative to the start of the IBufferView - uint32_t relByteoffset; - // TODO: some info about format class - - auto operator<=>(const TexelBuffer&) const = default; - }; - struct StorageTexelBuffer : DescriptorCommon - { - // relative to the start of the IBufferView - uint32_t relByteoffset; - E_FORMAT format; - - auto operator<=>(const StorageTexelBuffer&) const = default; - }; - struct Buffer : DescriptorCommon - { - // relative to the offset of the descriptor when bound (true byteoffset = static descriptor-set defined + dynamic [if enabled] + this value) - uint32_t relByteoffset; - uint32_t bytesize; - - auto operator<=>(const Buffer&) const = default; - }; - struct PushConstant - { - uint32_t byteOffset; - - auto operator<=>(const PushConstant&) const = default; - }; - enum class E_TYPE: uint8_t - { - ET_SAMPLER = nbl::core::to_underlying(IDescriptor::E_TYPE::ET_SAMPLER), - ET_COMBINED_IMAGE_SAMPLER = nbl::core::to_underlying(IDescriptor::E_TYPE::ET_COMBINED_IMAGE_SAMPLER), - ET_SAMPLED_IMAGE = nbl::core::to_underlying(IDescriptor::E_TYPE::ET_SAMPLED_IMAGE), - ET_STORAGE_IMAGE = nbl::core::to_underlying(IDescriptor::E_TYPE::ET_STORAGE_IMAGE), - ET_UNIFORM_TEXEL_BUFFER = nbl::core::to_underlying(IDescriptor::E_TYPE::ET_UNIFORM_TEXEL_BUFFER), - ET_STORAGE_TEXEL_BUFFER = nbl::core::to_underlying(IDescriptor::E_TYPE::ET_STORAGE_TEXEL_BUFFER), - ET_UNIFORM_BUFFER = nbl::core::to_underlying(IDescriptor::E_TYPE::ET_UNIFORM_BUFFER), - ET_STORAGE_BUFFER = nbl::core::to_underlying(IDescriptor::E_TYPE::ET_STORAGE_BUFFER), - ET_INPUT_ATTACHMENT = nbl::core::to_underlying(IDescriptor::E_TYPE::ET_INPUT_ATTACHMENT), - ET_PUSH_CONSTANT = 11 - }; - E_TYPE type; - IShader::E_SHADER_STAGE shaderAccessFlags; - union - { - CombinedImageSampler combinedImageSampler; - StorageImage storageImage; - TexelBuffer texelBuffer; - StorageTexelBuffer storageTexelBuffer; - Buffer uniformBufferObject; - Buffer storageBufferObject; - PushConstant pushConstant; - }; - - inline bool operator!=(const ShaderInput& other) const - { - return !std::memcmp(this, &other, sizeof(other)); - } - }; - - //! A non exhaustive list of commonly used shader input semantics - enum E_COMMON_SHADER_INPUT - { - //! core::matrix4SIMD giving the total projection onto the screen from model-space coordinates - ECSI_WORLD_VIEW_PROJ, - //! core::matrix4SIMD giving the mapping from view-space into the pre-divide NDC space - ECSI_PROJ, - //! core::matrix3x4SIMD giving the view-space transformation from model-space coordinates - ECSI_WORLD_VIEW, - //! core::matrix3x4SIMD giving the view-space transformation from world-space - ECSI_VIEW, - //! core::matrix3x4SIMD giving the world-space transformation from model-space (last column is object world-space-position) - ECSI_WORLD, - //! core::matrix4SIMD giving the total projection to model-space coordinates from screen-space - ECSI_WORLD_VIEW_PROJ_INVERSE, - //! core::matrix4SIMD giving the mapping from the pre-divide NDC space into view-space - ECSI_PROJ_INVERSE, - //! core::matrix3x4SIMD giving the model-space transformation from view-space coordinates - ECSI_WORLD_VIEW_INVERSE, - //! core::matrix3x4SIMD giving the world-space transformation from view-space (last column is camera world-space-position) - ECSI_VIEW_INVERSE, - //! core::matrix3x4SIMD giving the model-space transformation from world-space - ECSI_WORLD_INVERSE, - //! transpose of core::matrix4SIMD giving the total projection to model-space coordinates from screen-space - ECSI_WORLD_VIEW_PROJ_INVERSE_TRANSPOSE, - //! transpose of core::matrix4SIMD giving the mapping from the pre-divide NDC space into view-space - ECSI_PROJ_INVERSE_TRANSPOSE, - //! transpose of core::matrix3x4SIMD giving the model-space transformation from view-space coordinates (upper 3x3 matrix can be used instead of `gl_NormalMatrix`) - ECSI_WORLD_VIEW_INVERSE_TRANSPOSE, - //! transpose of core::matrix3x4SIMD giving the world-space transformation from view-space (last row is camera world-space-position) - ECSI_VIEW_INVERSE_TRANSPOSE, - //! transpose of core::matrix3x4SIMD giving the model-space transformation from world-space (upper 3x3 matrix can transform model space normals to world space) - ECSI_WORLD_INVERSE_TRANSPOSE, - - //! a simple non-filtered environment map as a cubemap - ECSI_ENVIRONMENT_CUBEMAP, - - //! For internal - ECSI_COUNT - }; - //! Tie the semantics to inputs - struct ShaderInputSemantic - { - E_COMMON_SHADER_INPUT type; - ShaderInput descriptorSection; - - inline bool operator!=(const ShaderInputSemantic& other) const - { - return type != other.type || descriptorSection != other.descriptorSection; - } - }; - core::SRange m_inputSemantics; - - inline bool operator!=(const IRenderpassIndependentPipelineMetadata& other) const - { - if (m_inputSemantics.empty()) - return false; - - bool status = false; - for (size_t i = 0; i < m_inputSemantics.size(); ++i) - status = m_inputSemantics.begin()[i] != other.m_inputSemantics.begin()[i]; - - return status; - } - - protected: - IRenderpassIndependentPipelineMetadata() : m_inputSemantics(nullptr,nullptr) {} - IRenderpassIndependentPipelineMetadata(core::SRange&& _inputSemantics) : m_inputSemantics(std::move(_inputSemantics)) {} - virtual ~IRenderpassIndependentPipelineMetadata() = default; - - //! - inline IRenderpassIndependentPipelineMetadata& operator=(IRenderpassIndependentPipelineMetadata&& other) - { - m_inputSemantics = std::move(other.m_inputSemantics); - return *this; - } -}; - - -} -} - -#endif diff --git a/include/nbl/asset/utils/CCPUMeshPackerV1.h b/include/nbl/asset/utils/CCPUMeshPackerV1.h deleted file mode 100644 index 23b2da563a..0000000000 --- a/include/nbl/asset/utils/CCPUMeshPackerV1.h +++ /dev/null @@ -1,538 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_C_CPU_MESH_PACKER_H_INCLUDED__ -#define __NBL_ASSET_C_CPU_MESH_PACKER_H_INCLUDED__ - -#include -#include -#include - -namespace nbl -{ -namespace asset -{ - -#if 0 // REWRITE -template -class CCPUMeshPackerV1 final : public IMeshPacker -{ - using base_t = IMeshPacker; - using Triangle = typename base_t::Triangle; - using TriangleBatches = typename base_t::TriangleBatches; - using IdxBufferParams = typename base_t::IdxBufferParams; - -public: - struct AllocationParams : IMeshPackerBase::AllocationParamsCommon - { - // Maximum byte size of per instance vertex data allocation - size_t perInstanceVertexBuffSupportedByteSize = 33554432ull; /* 32MB*/ - - // Minimum bytes of per instance vertex data allocated per allocation - size_t perInstanceVertexBufferMinAllocByteSize = 32ull; - }; - - struct ReservedAllocationMeshBuffers : IMeshPackerBase::ReservedAllocationMeshBuffersBase - { - uint32_t instanceAllocationOffset; - uint32_t instanceAllocationReservedSize; - uint32_t vertexAllocationOffset; - uint32_t vertexAllocationReservedSize; - }; - - struct PackerDataStore : base_t::template PackerDataStoreCommon - { - SBufferBinding vertexBufferBindings[SVertexInputParams::MAX_ATTR_BUF_BINDING_COUNT] = {}; - SBufferBinding indexBuffer; - - SVertexInputParams vertexInputParams; - }; - - template - struct MeshPackerConfigParams - { - SVertexInputParams vertexInputParams; - core::SRange belongingMeshes; // pointers to sections of `sortedMeshBuffersOut` - }; - -public: - CCPUMeshPackerV1(const SVertexInputParams& preDefinedLayout, const AllocationParams& allocParams, uint16_t minTriangleCountPerMDIData = 256u, uint16_t maxTriangleCountPerMDIData = 1024u); - - ~CCPUMeshPackerV1() - { - if(isInstancingEnabled) - _NBL_ALIGNED_FREE(m_perInsVtxBuffAlctrResSpc); - } - - template - ReservedAllocationMeshBuffers alloc(const MeshBufferIterator mbBegin, const MeshBufferIterator mbEnd); - - void free(const ReservedAllocationMeshBuffers& ramb) - { - if (ramb.indexAllocationOffset != base_t::INVALID_ADDRESS) - base_t::m_idxBuffAlctr.free_addr(ramb.indexAllocationOffset, ramb.indexAllocationReservedCnt); - - if (ramb.mdiAllocationOffset != base_t::INVALID_ADDRESS) - base_t::m_MDIDataAlctr.free_addr(ramb.mdiAllocationOffset, ramb.mdiAllocationReservedCnt); - - if (ramb.vertexAllocationOffset != base_t::INVALID_ADDRESS) - base_t::m_vtxBuffAlctr.free_addr(ramb.vertexAllocationOffset, ramb.vertexAllocationReservedSize); - - if (ramb.instanceAllocationOffset != base_t::INVALID_ADDRESS) - base_t::m_vtxBuffAlctr.free_addr(ramb.instanceAllocationOffset, ramb.instanceAllocationReservedSize); - } - - //needs to be called before first `commit` - void instantiateDataStorage(); - - template - IMeshPackerBase::PackedMeshBufferData commit(const MeshBufferIterator mbBegin, const MeshBufferIterator mbEnd, ReservedAllocationMeshBuffers& ramb, core::aabbox3df* aabbs); - - inline PackerDataStore& getPackerDataStore() { return m_output; }; - - // returns number of distinct mesh packers needed to pack the meshes and a sorted list of meshes by the meshpacker ID they should be packed into, as well as the parameters for the packers - // `packerParamsOut` should be big enough to fit `std::distance(begin,end)` entries, the return value will tell you how many were actually written - template - static uint32_t getPackerCreationParamsFromMeshBufferRange(const Iterator begin, const Iterator end, Iterator sortedMeshBuffersOut, - MeshPackerConfigParams* packerParamsOut); - - //! shrinks byte size of all output buffers, so they are large enough to fit currently allocated contents. Call this function before `instantiateDataStorage` - virtual void shrinkOutputBuffersSize() override - { - base_t::shrinkOutputBuffersSize(); - - uint32_t perInsBuffNewSize = m_perInsVtxBuffAlctr.safe_shrink_size(0u, base_t::alctrTraits::max_alignment(m_perInsVtxBuffAlctr)); - - if (isInstancingEnabled) - { - const void* oldReserved = base_t::alctrTraits::getReservedSpacePtr(m_perInsVtxBuffAlctr); - m_perInsVtxBuffAlctr = core::GeneralpurposeAddressAllocator(perInsBuffNewSize, std::move(m_perInsVtxBuffAlctr), _NBL_ALIGNED_MALLOC(base_t::alctrTraits::reserved_size(perInsBuffNewSize, m_perInsVtxBuffAlctr), _NBL_SIMD_ALIGNMENT)); - _NBL_ALIGNED_FREE(const_cast(oldReserved)); - } - } - -private: - static bool cmpVtxInputParams(const SVertexInputParams& lhs, const SVertexInputParams& rhs); - -private: - PackerDataStore m_output; - - uint32_t m_vtxSize; - uint32_t m_perInsVtxSize; - - bool isInstancingEnabled; - void* m_perInsVtxBuffAlctrResSpc; - core::GeneralpurposeAddressAllocator m_perInsVtxBuffAlctr; - - _NBL_STATIC_INLINE_CONSTEXPR ReservedAllocationMeshBuffers invalidReservedAllocationMeshBuffers{ base_t::INVALID_ADDRESS, 0, 0, 0, 0, 0, 0, 0 }; - -}; - -template -CCPUMeshPackerV1::CCPUMeshPackerV1(const SVertexInputParams& preDefinedLayout, const AllocationParams& allocParams, uint16_t minTriangleCountPerMDIData, uint16_t maxTriangleCountPerMDIData) - :IMeshPacker(minTriangleCountPerMDIData, maxTriangleCountPerMDIData), - m_perInsVtxBuffAlctrResSpc(nullptr) - -{ - m_output.vertexInputParams.enabledAttribFlags = preDefinedLayout.enabledAttribFlags; - m_output.vertexInputParams.enabledBindingFlags = preDefinedLayout.enabledAttribFlags; - memcpy(m_output.vertexInputParams.attributes, preDefinedLayout.attributes, sizeof(m_output.vertexInputParams.attributes)); - - //1 attrib enabled == 1 binding - for (uint16_t attrBit = 0x0001, location = 0; location < SVertexInputParams::MAX_ATTR_BUF_BINDING_COUNT; attrBit <<= 1, location++) - { - if (m_output.vertexInputParams.enabledAttribFlags & attrBit) - { - m_output.vertexInputParams.attributes[location].binding = location; - m_output.vertexInputParams.attributes[location].relativeOffset = 0u; - m_output.vertexInputParams.bindings[location].stride = getTexelOrBlockBytesize(static_cast(m_output.vertexInputParams.attributes[location].format)); - m_output.vertexInputParams.bindings[location].inputRate = preDefinedLayout.bindings[preDefinedLayout.attributes[location].binding].inputRate; - } - } - - m_vtxSize = base_t::calcVertexSize(preDefinedLayout, E_VERTEX_INPUT_RATE::EVIR_PER_VERTEX); - - m_perInsVtxSize = base_t::calcVertexSize(preDefinedLayout, E_VERTEX_INPUT_RATE::EVIR_PER_INSTANCE); - if (m_perInsVtxSize) - { - isInstancingEnabled = true; - m_perInsVtxBuffAlctrResSpc = _NBL_ALIGNED_MALLOC(core::GeneralpurposeAddressAllocator::reserved_size(alignof(std::max_align_t), allocParams.perInstanceVertexBuffSupportedByteSize / m_perInsVtxSize, allocParams.perInstanceVertexBufferMinAllocByteSize), _NBL_SIMD_ALIGNMENT); - assert(m_perInsVtxBuffAlctrResSpc != nullptr); - m_perInsVtxBuffAlctr = core::GeneralpurposeAddressAllocator(m_perInsVtxBuffAlctrResSpc, 0u, 0u, alignof(std::max_align_t), allocParams.perInstanceVertexBuffSupportedByteSize / m_perInsVtxSize, allocParams.perInstanceVertexBufferMinAllocByteSize); - } - else - { - isInstancingEnabled = false; - } - - base_t::initializeCommonAllocators( - { - allocParams.indexBuffSupportedCnt, - m_vtxSize ? allocParams.vertexBuffSupportedByteSize / m_vtxSize : 0ull, - allocParams.MDIDataBuffSupportedCnt, - allocParams.indexBufferMinAllocCnt, - allocParams.vertexBufferMinAllocByteSize, - allocParams.MDIDataBuffMinAllocCnt - } - ); -} - -template -//`Iterator` may be only an Iterator or pointer to pointer -//allocation should be happening even if processed mesh buffer doesn't have attribute that was declared in pre defined `SVertexInputParams`, if mesh buffer has any attributes that are not declared in pre defined `SVertexInputParams` then these should be always ignored -/* - Requirements for input mesh buffers: - - attributes bound to the same binding must have identical format -*/ -template -typename CCPUMeshPackerV1::ReservedAllocationMeshBuffers CCPUMeshPackerV1::alloc(const MeshBufferIterator mbBegin, const MeshBufferIterator mbEnd) -{ - //validation - for (auto it = mbBegin; it != mbEnd; it++) - { - const auto& mbVtxInputParams = (*it)->getPipeline()->getVertexInputParams(); - for (uint16_t attrBit = 0x0001, location = 0; location < SVertexInputParams::MAX_ATTR_BUF_BINDING_COUNT; attrBit <<= 1, location++) - { - if (!(m_output.vertexInputParams.enabledAttribFlags & mbVtxInputParams.enabledAttribFlags & attrBit)) - continue; - - if (mbVtxInputParams.attributes[location].format != m_output.vertexInputParams.attributes[location].format || - mbVtxInputParams.bindings[mbVtxInputParams.attributes[location].binding].inputRate != m_output.vertexInputParams.bindings[location].inputRate) - { - assert(false); - return invalidReservedAllocationMeshBuffers; - } - } - } - - size_t idxCnt = 0u; - size_t vtxCnt = 0u; - size_t perInsVtxCnt = 0u; - for (auto it = mbBegin; it != mbEnd; it++) - { - ICPUMeshBuffer* mb = *it; - idxCnt += base_t::calcIdxCntAfterConversionToTriangleList(mb); - vtxCnt += base_t::calcVertexCountBoundWithBatchDuplication(mb); - perInsVtxCnt += mb->getInstanceCount(); - } - - const uint32_t minIdxCntPerPatch = base_t::m_minTriangleCountPerMDIData * 3; - - uint32_t possibleMDIStructsNeededCnt = 0u; - for (auto it = mbBegin; it != mbEnd; it++) - possibleMDIStructsNeededCnt += ((*it)->getIndexCount() + minIdxCntPerPatch - 1) / minIdxCntPerPatch; - - uint32_t MDIAllocAddr = base_t::INVALID_ADDRESS; - uint32_t idxAllocAddr = base_t::INVALID_ADDRESS; - uint32_t vtxAllocAddr = base_t::INVALID_ADDRESS; - uint32_t perInsVtxAllocAddr = base_t::INVALID_ADDRESS; - - MDIAllocAddr = base_t::m_MDIDataAlctr.alloc_addr(possibleMDIStructsNeededCnt, 1u); - if (MDIAllocAddr == base_t::INVALID_ADDRESS) - { - _NBL_DEBUG_BREAK_IF(true); - return invalidReservedAllocationMeshBuffers; - } - - idxAllocAddr = base_t::m_idxBuffAlctr.alloc_addr(idxCnt, 1u); - if (idxAllocAddr == base_t::INVALID_ADDRESS) - { - _NBL_DEBUG_BREAK_IF(true); - - base_t::m_MDIDataAlctr.free_addr(MDIAllocAddr, possibleMDIStructsNeededCnt); - - return invalidReservedAllocationMeshBuffers; - } - - bool arePerVtxAttribsEnabled = base_t::alctrTraits::get_total_size(base_t::m_vtxBuffAlctr) == 0 ? false : true; - if (arePerVtxAttribsEnabled) - { - vtxAllocAddr = base_t::m_vtxBuffAlctr.alloc_addr(vtxCnt * m_vtxSize, 1u); - if (vtxAllocAddr == base_t::INVALID_ADDRESS) - { - _NBL_DEBUG_BREAK_IF(true); - - base_t::m_MDIDataAlctr.free_addr(MDIAllocAddr, possibleMDIStructsNeededCnt); - base_t::m_idxBuffAlctr.free_addr(idxAllocAddr, idxCnt); - - return invalidReservedAllocationMeshBuffers; - } - } - - if (isInstancingEnabled) - { - perInsVtxAllocAddr = m_perInsVtxBuffAlctr.alloc_addr(perInsVtxCnt * m_perInsVtxSize, 1u); - if (perInsVtxAllocAddr == base_t::INVALID_ADDRESS) - { - _NBL_DEBUG_BREAK_IF(true); - - base_t::m_MDIDataAlctr.free_addr(MDIAllocAddr, possibleMDIStructsNeededCnt); - base_t::m_idxBuffAlctr.free_addr(idxAllocAddr, idxCnt); - base_t::m_vtxBuffAlctr.free_addr(vtxAllocAddr, vtxCnt * m_vtxSize); - - return invalidReservedAllocationMeshBuffers; - } - } - - ReservedAllocationMeshBuffers result; - result.mdiAllocationOffset = MDIAllocAddr; - result.mdiAllocationReservedCnt = possibleMDIStructsNeededCnt; - result.indexAllocationOffset = idxAllocAddr; - result.indexAllocationReservedCnt = idxCnt; - result.instanceAllocationOffset = perInsVtxAllocAddr; - result.instanceAllocationReservedSize = perInsVtxAllocAddr == base_t::INVALID_ADDRESS ? 0u : perInsVtxCnt * m_perInsVtxSize; - result.vertexAllocationOffset = vtxAllocAddr; - result.vertexAllocationReservedSize = vtxAllocAddr == base_t::INVALID_ADDRESS ? 0u : vtxCnt * m_vtxSize; - - return result; -} - -template -void CCPUMeshPackerV1::instantiateDataStorage() -{ - const size_t MDIDataBuffSupportedByteSize = base_t::alctrTraits::get_total_size(base_t::m_MDIDataAlctr) * sizeof(MDIStructType); - const size_t idxBuffSupportedByteSize = base_t::alctrTraits::get_total_size(base_t::m_idxBuffAlctr) * sizeof(uint16_t); - const size_t vtxBuffSupportedByteSize = base_t::alctrTraits::get_total_size(base_t::m_vtxBuffAlctr); - const size_t perInsBuffSupportedByteSize = base_t::alctrTraits::get_total_size(base_t::m_vtxBuffAlctr); - - m_output.MDIDataBuffer = core::make_smart_refctd_ptr(MDIDataBuffSupportedByteSize); - m_output.indexBuffer.buffer = core::make_smart_refctd_ptr(idxBuffSupportedByteSize); - - core::smart_refctd_ptr unifiedVtxBuff = core::make_smart_refctd_ptr(vtxBuffSupportedByteSize); - core::smart_refctd_ptr unifiedInsBuff = core::make_smart_refctd_ptr(perInsBuffSupportedByteSize); - - //divide unified vtx buffers - //proportions: sizeOfAttr1 : sizeOfAttr2 : ... : sizeOfAttrN - std::array attrSizeArray; - - uint32_t vtxBufferOffset = 0u; - const uint32_t maxVtxCnt = m_vtxSize == 0u ? 0u : vtxBuffSupportedByteSize / m_vtxSize; - - uint32_t perInsBuffOffset = 0u; - const uint32_t maxPerInsVtxCnt = m_perInsVtxSize == 0u ? 0u : perInsBuffSupportedByteSize / m_perInsVtxSize; - - for (uint16_t attrBit = 0x0001, location = 0; location < SVertexInputParams::MAX_ATTR_BUF_BINDING_COUNT; attrBit <<= 1, location++) - { - if (m_output.vertexInputParams.enabledAttribFlags & attrBit) - { - auto attrib = m_output.vertexInputParams.attributes[location]; - auto binding = m_output.vertexInputParams.bindings[attrib.binding]; - - if (binding.inputRate == EVIR_PER_VERTEX) - { - m_output.vertexBufferBindings[location] = { vtxBufferOffset, unifiedVtxBuff }; - vtxBufferOffset += asset::getTexelOrBlockBytesize(static_cast(attrib.format)) * maxVtxCnt; - } - else if (binding.inputRate == EVIR_PER_INSTANCE) - { - m_output.vertexBufferBindings[location] = { perInsBuffOffset, unifiedInsBuff }; - perInsBuffOffset += asset::getTexelOrBlockBytesize(static_cast(attrib.format)) * maxPerInsVtxCnt; - } - } - } - -} - -template -template -IMeshPackerBase::PackedMeshBufferData CCPUMeshPackerV1::commit(const MeshBufferIterator mbBegin, const MeshBufferIterator mbEnd, CCPUMeshPackerV1::ReservedAllocationMeshBuffers& ramb, core::aabbox3df* aabbs) -{ - MDIStructType* mdiBuffPtr = static_cast(m_output.MDIDataBuffer->getPointer()) + ramb.mdiAllocationOffset; - uint16_t* indexBuffPtr = static_cast(m_output.indexBuffer.buffer->getPointer()) + ramb.indexAllocationOffset; - size_t verticesAddedToUnifiedBufferCnt = 0ull; - size_t instancesAddedCnt = 0ull; - - uint32_t MDIStructsAddedCnt = 0u; - - size_t batchFirstIdx = ramb.indexAllocationOffset; - size_t batchBaseVtx = ramb.vertexAllocationOffset; - - for (auto it = mbBegin; it != mbEnd; it++) - { - const auto mbPrimitiveType = (*it)->getPipeline()->getPrimitiveAssemblyParams().primitiveType; - - IdxBufferParams idxBufferParams = base_t::createNewIdxBufferParamsForNonTriangleListTopologies(*it); - - TriangleBatches triangleBatches = base_t::constructTriangleBatches(*it, idxBufferParams, aabbs); - const auto& mbVtxInputParams = (*it)->getPipeline()->getVertexInputParams(); - - const uint32_t batchCnt = triangleBatches.ranges.size() - 1u; - for (uint32_t i = 0u; i < batchCnt; i++) - { - auto batchBegin = triangleBatches.ranges[i]; - auto batchEnd = triangleBatches.ranges[i + 1]; - - const uint32_t triangleInBatchCnt = std::distance(batchBegin, batchEnd); - const uint32_t idxInBatchCnt = 3 * triangleInBatchCnt; - - core::unordered_map usedVertices = base_t::constructNewIndicesFromTriangleBatchAndUpdateUnifiedIndexBuffer(triangleBatches, i, indexBuffPtr); - - //copy deinterleaved vertices into unified vertex buffer - for (uint16_t attrBit = 0x0001, location = 0; location < SVertexInputParams::MAX_ATTR_BUF_BINDING_COUNT; attrBit <<= 1, location++) - { - if (!(m_output.vertexInputParams.enabledAttribFlags & mbVtxInputParams.enabledAttribFlags & attrBit)) - continue; - - SVertexInputAttribParams attrib = m_output.vertexInputParams.attributes[location]; - SBufferBinding& vtxBuffBind = m_output.vertexBufferBindings[location]; - const E_VERTEX_INPUT_RATE inputRate = m_output.vertexInputParams.bindings[attrib.binding].inputRate; - uint8_t* dstAttrPtr = static_cast(vtxBuffBind.buffer->getPointer()) + vtxBuffBind.offset; - const size_t attrSize = asset::getTexelOrBlockBytesize(static_cast(attrib.format)); - - if (inputRate == EVIR_PER_VERTEX) - { - dstAttrPtr += (ramb.vertexAllocationOffset + verticesAddedToUnifiedBufferCnt) * attrSize; - base_t::deinterleaveAndCopyAttribute(*it, location, usedVertices, dstAttrPtr); - } - else if (inputRate == EVIR_PER_INSTANCE) - { - dstAttrPtr += (ramb.instanceAllocationOffset + instancesAddedCnt) * attrSize; - base_t::deinterleaveAndCopyPerInstanceAttribute(*it, location, dstAttrPtr); - } - } - - //construct mdi data - MDIStructType MDIData; - MDIData.count = idxInBatchCnt; - MDIData.instanceCount = isInstancingEnabled ? (*it)->getInstanceCount() : 1u; - MDIData.firstIndex = batchFirstIdx; - MDIData.baseVertex = batchBaseVtx; //possible overflow? - MDIData.baseInstance = isInstancingEnabled ? instancesAddedCnt : 0u; - - *mdiBuffPtr = MDIData; - mdiBuffPtr++; - MDIStructsAddedCnt++; - - batchFirstIdx += idxInBatchCnt; - batchBaseVtx += usedVertices.size(); - - verticesAddedToUnifiedBufferCnt += usedVertices.size(); - } - - instancesAddedCnt += (*it)->getInstanceCount(); - } - - return { ramb.mdiAllocationOffset, MDIStructsAddedCnt }; -} - -template -static bool CCPUMeshPackerV1::cmpVtxInputParams(const SVertexInputParams& lhs, const SVertexInputParams& rhs) -{ - if (lhs.enabledAttribFlags != rhs.enabledAttribFlags) - return false; - - for (uint16_t attrBit = 0x0001, location = 0; location < SVertexInputParams::MAX_ATTR_BUF_BINDING_COUNT; attrBit <<= 1, location++) - { - if (!(attrBit & lhs.enabledAttribFlags)) - continue; - - if (lhs.attributes[location].format != rhs.attributes[location].format || - lhs.bindings[lhs.attributes[location].binding].inputRate != rhs.bindings[rhs.attributes[location].binding].inputRate) - return false; - } - - return true; -} - -template -template -static uint32_t CCPUMeshPackerV1::getPackerCreationParamsFromMeshBufferRange(const Iterator begin, const Iterator end, Iterator sortedMeshBuffersOut, - MeshPackerConfigParams* packerParamsOut) -{ - assert(begin <= end); - if (begin == end) - return 0; - - uint32_t packersNeeded = 1u; - - typename IMeshPackerBase::MeshPackerConfigParams firstInpuParams - { - (*begin)->getPipeline()->getVertexInputParams(), - SRange(sortedMeshBuffersOut, sortedMeshBuffersOut) - }; - memcpy(packerParamsOut, &firstInpuParams, sizeof(SVertexInputParams)); - - //fill array - auto test1 = std::distance(begin, end); - auto* packerParamsOutEnd = packerParamsOut + 1u; - for (Iterator it = begin + 1; it != end; it++) - { - auto& currMeshVtxInputParams = (*it)->getPipeline()->getVertexInputParams(); - - bool alreadyInserted = false; - for (auto* packerParamsIt = packerParamsOut; packerParamsIt != packerParamsOutEnd; packerParamsIt++) - { - alreadyInserted = cmpVtxInputParams(packerParamsIt->vertexInputParams, currMeshVtxInputParams); - - if (alreadyInserted) - break; - } - - if (!alreadyInserted) - { - packersNeeded++; - - typename IMeshPackerBase::MeshPackerConfigParams configParams - { - currMeshVtxInputParams, - SRange(sortedMeshBuffersOut, sortedMeshBuffersOut) - }; - memcpy(packerParamsOutEnd, &configParams, sizeof(SVertexInputParams)); - packerParamsOutEnd++; - } - } - - auto getIndexOfArrayElement = [&](const SVertexInputParams& vtxInputParams) -> int32_t - { - int32_t offset = 0u; - for (auto* it = packerParamsOut; it != packerParamsOutEnd; it++, offset++) - { - if (cmpVtxInputParams(vtxInputParams, it->vertexInputParams)) - return offset; - - if (it == packerParamsOut - 1) - return -1; - } - }; - - //sort meshes by SVertexInputParams - const Iterator sortedMeshBuffersOutEnd = sortedMeshBuffersOut + std::distance(begin, end); - - std::copy(begin, end, sortedMeshBuffersOut); - std::sort(sortedMeshBuffersOut, sortedMeshBuffersOutEnd, - [&](const ICPUMeshBuffer* lhs, const ICPUMeshBuffer* rhs) - { - return getIndexOfArrayElement(lhs->getPipeline()->getVertexInputParams()) < getIndexOfArrayElement(rhs->getPipeline()->getVertexInputParams()); - } - ); - - //set ranges - Iterator sortedMeshBuffersIt = sortedMeshBuffersOut; - for (auto* inputParamsIt = packerParamsOut; inputParamsIt != packerParamsOutEnd; inputParamsIt++) - { - Iterator firstMBForThisRange = sortedMeshBuffersIt; - Iterator lastMBForThisRange = sortedMeshBuffersIt; - for (Iterator it = firstMBForThisRange; it != sortedMeshBuffersOutEnd; it++) - { - if (!cmpVtxInputParams(inputParamsIt->vertexInputParams, (*it)->getPipeline()->getVertexInputParams())) - { - lastMBForThisRange = it; - break; - } - } - - if (inputParamsIt == packerParamsOutEnd - 1) - lastMBForThisRange = sortedMeshBuffersOutEnd; - - inputParamsIt->belongingMeshes = SRange(firstMBForThisRange, lastMBForThisRange); - sortedMeshBuffersIt = lastMBForThisRange; - } - - return packersNeeded; -} -#endif -} -} - -#endif diff --git a/include/nbl/asset/utils/CCPUMeshPackerV2.h b/include/nbl/asset/utils/CCPUMeshPackerV2.h deleted file mode 100644 index 83308ab82b..0000000000 --- a/include/nbl/asset/utils/CCPUMeshPackerV2.h +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_C_CPU_MESH_PACKER_V2_H_INCLUDED__ -#define __NBL_ASSET_C_CPU_MESH_PACKER_V2_H_INCLUDED__ - -#include -#include - -namespace nbl -{ -namespace asset -{ -#if 0 // REWRITE -template -class CCPUMeshPackerV2 final : public IMeshPackerV2 -{ - using base_t = IMeshPackerV2; - using Triangle = typename base_t::Triangle; - using TriangleBatches = typename base_t::TriangleBatches; - using IdxBufferParams = typename base_t::base_t::IdxBufferParams; - - public: - using AllocationParams = IMeshPackerBase::AllocationParamsCommon; - using PackerDataStore = typename base_t::PackerDataStore; - using ReservedAllocationMeshBuffers = typename base_t::ReservedAllocationMeshBuffers; - using AttribAllocParams = typename base_t::AttribAllocParams; - using CombinedDataOffsetTable = typename base_t::CombinedDataOffsetTable; - - public: - CCPUMeshPackerV2(const AllocationParams& allocParams, const IMeshPackerV2Base::SupportedFormatsContainer& formats, uint16_t minTriangleCountPerMDIData = 256u, uint16_t maxTriangleCountPerMDIData = 1024u) - : base_t(allocParams, formats, minTriangleCountPerMDIData, maxTriangleCountPerMDIData) - {} - - void instantiateDataStorage(); - - /** - \return number of mdi structs created for mesh buffer range described by mbBegin .. mbEnd, 0 if commit failed or mbBegin == mbEnd - */ - template - uint32_t commit(IMeshPackerBase::PackedMeshBufferData* pmbdOut, CombinedDataOffsetTable* cdotOut, core::aabbox3df* aabbs, ReservedAllocationMeshBuffers* rambIn, const MeshBufferIterator mbBegin, const MeshBufferIterator mbEnd); - - inline std::pair getDescriptorSetWritesForUTB( - ICPUDescriptorSet::SWriteDescriptorSet* outWrites, ICPUDescriptorSet::SDescriptorInfo* outInfo, ICPUDescriptorSet* dstSet, - const typename base_t::DSLayoutParamsUTB& params = {} - ) const - { - auto createBufferView = [&](core::smart_refctd_ptr&& buff, E_FORMAT format) -> core::smart_refctd_ptr - { - return core::make_smart_refctd_ptr(std::move(buff),format); - }; - return base_t::getDescriptorSetWritesForUTB(outWrites,outInfo,dstSet,createBufferView,params); - } -}; - -template -void CCPUMeshPackerV2::instantiateDataStorage() -{ - const uint32_t MDIDataBuffByteSize = base_t::m_MDIDataAlctr.get_total_size() * sizeof(MDIStructType); - const uint32_t idxBuffByteSize = base_t::m_idxBuffAlctr.get_total_size() * sizeof(uint16_t); - const uint32_t vtxBuffByteSize = base_t::m_vtxBuffAlctr.get_total_size(); - - base_t::m_packerDataStore.MDIDataBuffer = core::make_smart_refctd_ptr(MDIDataBuffByteSize); - base_t::m_packerDataStore.indexBuffer = core::make_smart_refctd_ptr(idxBuffByteSize); - base_t::m_packerDataStore.vertexBuffer = core::make_smart_refctd_ptr(vtxBuffByteSize); -} - -/* - @param pmbdOut size of this array has to be >= std::distance(mbBegin, mbEnd) - @param cdotOut size of this array has to be >= IMeshPackerV2::calcMDIStructMaxCount(mbBegin, mbEnd) -*/ -template -template -uint32_t CCPUMeshPackerV2::commit(IMeshPackerBase::PackedMeshBufferData* pmbdOut, CombinedDataOffsetTable* cdotOut, core::aabbox3df* aabbs, ReservedAllocationMeshBuffers* rambIn, const MeshBufferIterator mbBegin, const MeshBufferIterator mbEnd) -{ - MDIStructType* mdiBuffPtr = static_cast(base_t::m_packerDataStore.MDIDataBuffer->getPointer()) + rambIn->mdiAllocationOffset; - - size_t i = 0ull; - uint32_t batchCntTotal = 0u; - for (auto it = mbBegin; it != mbEnd; it++) - { - const ReservedAllocationMeshBuffers& ramb = *(rambIn + i); - IMeshPackerBase::PackedMeshBufferData& pmbd = *(pmbdOut + i); - - //this is fucked up.. - //mdiAllocationOffset should be one for all mesh buffers in range defined by mbBegin .. mbEnd, otherwise things get fucked when there are random sizes of batches - //TODO: so modify ReservedAllocationMeshBuffers and free function - //MDIStructType* mdiBuffPtr = static_cast(m_packerDataStore.MDIDataBuffer->getPointer()) + ramb.mdiAllocationOffset; - uint16_t* indexBuffPtr = static_cast(base_t::m_packerDataStore.indexBuffer->getPointer()) + ramb.indexAllocationOffset; - - const auto& mbVtxInputParams = (*it)->getPipeline()->getVertexInputParams(); - const uint32_t insCnt = (*it)->getInstanceCount(); - - IdxBufferParams idxBufferParams = base_t::createNewIdxBufferParamsForNonTriangleListTopologies(*it); - - TriangleBatches triangleBatches = base_t::constructTriangleBatches(*it, idxBufferParams, aabbs); - - size_t batchFirstIdx = ramb.indexAllocationOffset; - size_t verticesAddedCnt = 0u; - - //TODO: check if mpv1 does redundand copies - std::array perInsAttribFromThisLocationWasCopied; - std::fill(perInsAttribFromThisLocationWasCopied.begin(), perInsAttribFromThisLocationWasCopied.end(), false); - - const uint32_t batchCnt = triangleBatches.ranges.size() - 1u; - for (uint32_t i = 0u; i < batchCnt; i++) - { - auto batchBegin = triangleBatches.ranges[i]; - auto batchEnd = triangleBatches.ranges[i+1]; - const uint32_t triangleInBatchCnt = std::distance(batchBegin,batchEnd); - constexpr uint32_t kIndicesPerTriangle = 3u; - const uint32_t idxInBatchCnt = triangleInBatchCnt*kIndicesPerTriangle; - - core::unordered_map usedVertices = base_t::constructNewIndicesFromTriangleBatchAndUpdateUnifiedIndexBuffer(triangleBatches, i, indexBuffPtr); - - //copy deinterleaved vertices into unified vertex buffer - for (uint16_t attrBit = 0x0001, location = 0; location < SVertexInputParams::MAX_ATTR_BUF_BINDING_COUNT; attrBit <<= 1, location++) - { - if (!(mbVtxInputParams.enabledAttribFlags & attrBit)) - continue; - - if (ramb.attribAllocParams[location].offset == base_t::INVALID_ADDRESS) - return 0u; - - const E_FORMAT attribFormat = static_cast(mbVtxInputParams.attributes[location].format); - const uint32_t attribSize = asset::getTexelOrBlockBytesize(attribFormat); - const uint32_t binding = mbVtxInputParams.attributes[location].binding; - const E_VERTEX_INPUT_RATE inputRate = mbVtxInputParams.bindings[binding].inputRate; - - uint8_t* dstAttrPtr = static_cast(base_t::m_packerDataStore.vertexBuffer->getPointer()) + ramb.attribAllocParams[location].offset; - - if (inputRate == EVIR_PER_VERTEX) - { - const uint32_t currBatchOffsetForPerVtxAttribs = verticesAddedCnt * attribSize; - dstAttrPtr += currBatchOffsetForPerVtxAttribs; - base_t::deinterleaveAndCopyAttribute(*it, location, usedVertices, dstAttrPtr); - } - if (inputRate == EVIR_PER_INSTANCE) - { - if (perInsAttribFromThisLocationWasCopied[location] == false) - { - base_t::deinterleaveAndCopyPerInstanceAttribute(*it, location, dstAttrPtr); - perInsAttribFromThisLocationWasCopied[location] = true; - } - } - - auto& utb = base_t::m_virtualAttribConfig.utbs[base_t::VirtualAttribConfig::getUTBArrayTypeFromFormat(attribFormat)]; - auto vtxFormatInfo = utb.find(attribFormat); - if (vtxFormatInfo==utb.end()) - return 0u; - - uint16_t vaArrayElement = vtxFormatInfo->second; - uint32_t vaOffset; - - if (inputRate == EVIR_PER_VERTEX) - vaOffset = ramb.attribAllocParams[location].offset / attribSize + verticesAddedCnt; - if (inputRate == EVIR_PER_INSTANCE) - vaOffset = ramb.attribAllocParams[location].offset / attribSize; - - cdotOut->attribInfo[location] = base_t::VirtualAttribute(vaArrayElement,vaOffset); - - } - - verticesAddedCnt += usedVertices.size(); - cdotOut++; - - //construct mdi data - MDIStructType MDIData; - MDIData.count = idxInBatchCnt; - MDIData.instanceCount = (*it)->getInstanceCount(); - MDIData.firstIndex = batchFirstIdx; - MDIData.baseVertex = 0u; - MDIData.baseInstance = 0u; - - *mdiBuffPtr = MDIData; - mdiBuffPtr++; - - batchFirstIdx += idxInBatchCnt; - } - - pmbd = { rambIn->mdiAllocationOffset+batchCntTotal, static_cast(batchCnt) }; - batchCntTotal += batchCnt; - - i++; - } - - return batchCntTotal; -} -#endif -} -} - -#endif \ No newline at end of file diff --git a/include/nbl/asset/utils/CDirQuantCacheBase.h b/include/nbl/asset/utils/CDirQuantCacheBase.h index 4057e3e4d9..5475f36932 100644 --- a/include/nbl/asset/utils/CDirQuantCacheBase.h +++ b/include/nbl/asset/utils/CDirQuantCacheBase.h @@ -244,7 +244,7 @@ struct CDirQuantCacheBase::value_type template -class CDirQuantCacheBase : public impl::CDirQuantCacheBase +class CDirQuantCacheBase : public virtual core::IReferenceCounted, public impl::CDirQuantCacheBase { public: template @@ -282,7 +282,7 @@ class CDirQuantCacheBase : public impl::CDirQuantCacheBase backup.swap(particularCache); CBufferPhmapInputArchive buffWrap(buffer); - bool loadingSuccess = particularCache.load(buffWrap); + bool loadingSuccess = particularCache.phmap_load(buffWrap); if (!replaceCurrentContents || !loadingSuccess) particularCache.merge(std::move(backup)); @@ -333,7 +333,7 @@ class CDirQuantCacheBase : public impl::CDirQuantCacheBase return false; CBufferPhmapOutputArchive buffWrap(buffer); - return std::get>(cache).dump(buffWrap); + return std::get>(cache).phmap_dump(buffWrap); } //! diff --git a/include/nbl/asset/utils/IGeometryCreator.h b/include/nbl/asset/utils/CGeometryCreator.h similarity index 54% rename from include/nbl/asset/utils/IGeometryCreator.h rename to include/nbl/asset/utils/CGeometryCreator.h index 0fcfefcb5e..87d7a0ef5e 100644 --- a/include/nbl/asset/utils/IGeometryCreator.h +++ b/include/nbl/asset/utils/CGeometryCreator.h @@ -1,45 +1,47 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_C_GEOMETRY_CREATOR_H_INCLUDED_ +#define _NBL_ASSET_C_GEOMETRY_CREATOR_H_INCLUDED_ -#ifndef __NBL_ASSET_I_GEOMETRY_CREATOR_H_INCLUDED__ -#define __NBL_ASSET_I_GEOMETRY_CREATOR_H_INCLUDED__ #include "nbl/core/declarations.h" -#include "nbl/asset/ICPUMesh.h" -#include "nbl/asset/utils/IMeshManipulator.h" - +#include "nbl/asset/utils/CPolygonGeometryManipulator.h" +// legacy, needs to be removed #include "SColor.h" -namespace nbl -{ -namespace asset + +namespace nbl::asset { //! Helper class for creating geometry on the fly. /** You can get an instance of this class through ISceneManager::getGeometryCreator() */ -class IGeometryCreator : public core::IReferenceCounted +class NBL_API2 CGeometryCreator final : public core::IReferenceCounted { - _NBL_INTERFACE_CHILD(IGeometryCreator) {} public: - struct return_type + struct SCreationParams { - SVertexInputParams inputParams; - SPrimitiveAssemblyParams assemblyParams; - SBufferBinding bindings[ICPUMeshBuffer::MAX_ATTR_BUF_BINDING_COUNT]; - SBufferBinding indexBuffer; - E_INDEX_TYPE indexType; - uint32_t indexCount; - core::aabbox3df bbox; + core::smart_refctd_ptr normalCache = nullptr; + core::smart_refctd_ptr quaternionCache = nullptr; }; + inline CGeometryCreator(SCreationParams&& params={}) : m_params(std::move(params)) + { + if (!m_params.normalCache) + m_params.normalCache = core::make_smart_refctd_ptr(); + if (!m_params.quaternionCache) + m_params.quaternionCache = core::make_smart_refctd_ptr(); + } + + // + const SCreationParams& getCreationParams() const {return m_params;} //! Creates a simple cube mesh. /** \param size Dimensions of the cube. \return Generated mesh. */ - virtual return_type createCubeMesh(const core::vector3df& size=core::vector3df(5.f,5.f,5.f)) const =0; + core::smart_refctd_ptr createCube(const hlsl::float32_t3 size={5.f,5.f,5.f}) const; //! Create an arrow mesh, composed of a cylinder and a cone. @@ -56,11 +58,11 @@ class IGeometryCreator : public core::IReferenceCounted \param colorCone color of the cone \return Generated mesh. */ - virtual return_type createArrowMesh(const uint32_t tesselationCylinder = 4, + core::smart_refctd_ptr createArrow(const uint32_t tesselationCylinder = 4, const uint32_t tesselationCone = 8, const float height = 1.f, const float cylinderHeight = 0.6f, const float widthCylinder = 0.05f, const float widthCone = 0.3f, const video::SColor colorCylinder = 0xFFFFFFFF, - const video::SColor colorCone = 0xFFFFFFFF, IMeshManipulator* const meshManipulatorOverride = nullptr) const =0; + const video::SColor colorCone = 0xFFFFFFFF) const; //! Create a sphere mesh. @@ -70,8 +72,8 @@ class IGeometryCreator : public core::IReferenceCounted \param polyCountY Number of quads used for the vertical tiling \return Generated mesh. */ - virtual return_type createSphereMesh(float radius = 5.f, - uint32_t polyCountX = 16, uint32_t polyCountY = 16, IMeshManipulator* const meshManipulatorOverride = nullptr) const =0; + core::smart_refctd_ptr createSphere(float radius = 5.f, + uint32_t polyCountX = 16, uint32_t polyCountY = 16, CQuantNormalCache* const quantNormalCacheOverride=nullptr) const; //! Create a cylinder mesh. /** @@ -83,9 +85,9 @@ class IGeometryCreator : public core::IReferenceCounted \param oblique (to be documented) \return Generated mesh. */ - virtual return_type createCylinderMesh(float radius, float length, + core::smart_refctd_ptr createCylinder(float radius, float length, uint32_t tesselation, - const video::SColor& color=video::SColor(0xffffffff), IMeshManipulator* const meshManipulatorOverride = nullptr) const =0; + const video::SColor& color=video::SColor(0xffffffff), CQuantNormalCache* const quantNormalCacheOverride=nullptr) const; //! Create a cone mesh. /** @@ -97,14 +99,14 @@ class IGeometryCreator : public core::IReferenceCounted \param oblique (to be documented) \return Generated mesh. */ - virtual return_type createConeMesh(float radius, float length, uint32_t tesselation, + core::smart_refctd_ptr createCone(float radius, float length, uint32_t tesselation, const video::SColor& colorTop=video::SColor(0xffffffff), const video::SColor& colorBottom=video::SColor(0xffffffff), - float oblique=0.f, IMeshManipulator* const meshManipulatorOverride = nullptr) const =0; + float oblique=0.f, CQuantNormalCache* const quantNormalCacheOverride=nullptr) const; - virtual return_type createRectangleMesh(const core::vector2df_SIMD& size = core::vector2df_SIMD(0.5f, 0.5f)) const = 0; + core::smart_refctd_ptr createRectangle(const hlsl::float32_t2 size={0.5f,0.5f}) const; - virtual return_type createDiskMesh(float radius, uint32_t tesselation) const = 0; + core::smart_refctd_ptr createDisk(const float radius, const uint32_t tesselation) const; //! Create a icosphere geometry /** @@ -113,12 +115,12 @@ class IGeometryCreator : public core::IReferenceCounted \param smooth Specifies whether vertecies should be built for smooth or flat shading. */ - virtual return_type createIcoSphere(float radius = 1.0f, uint32_t subdivision = 1, bool smooth = false) const = 0; + core::smart_refctd_ptr createIcoSphere(float radius=1.f, uint32_t subdivision=1, bool smooth=false) const; + private: + SCreationParams m_params; }; -} // end namespace asset -} // end namespace nbl - +} // end namespace nbl::asset #endif diff --git a/include/nbl/asset/utils/IMeshManipulator.h b/include/nbl/asset/utils/CPolygonGeometryManipulator.h similarity index 76% rename from include/nbl/asset/utils/IMeshManipulator.h rename to include/nbl/asset/utils/CPolygonGeometryManipulator.h index f84d85c75d..6f440536e5 100644 --- a/include/nbl/asset/utils/IMeshManipulator.h +++ b/include/nbl/asset/utils/CPolygonGeometryManipulator.h @@ -1,37 +1,59 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_C_POLYGON_GEOMETRY_MANIPULATOR_H_INCLUDED_ +#define _NBL_ASSET_C_POLYGON_GEOMETRY_MANIPULATOR_H_INCLUDED_ -#ifndef __NBL_ASSET_I_MESH_MANIPULATOR_H_INCLUDED__ -#define __NBL_ASSET_I_MESH_MANIPULATOR_H_INCLUDED__ - -#include -#include #include "nbl/core/declarations.h" -#include "vector3d.h" -#include "aabbox3d.h" - -#include "nbl/asset/ICPUMeshBuffer.h" -#include "nbl/asset/ICPUMesh.h" +#include "nbl/asset/ICPUPolygonGeometry.h" #include "nbl/asset/utils/CQuantNormalCache.h" #include "nbl/asset/utils/CQuantQuaternionCache.h" -namespace nbl +namespace nbl::asset { -namespace asset + +// TODO: move to its own header! +class NBL_API2 CGeometryManipulator { + public: + static inline void recomputeContentHash(const IGeometry::SDataView& view) + { + if (!view) + return; + view.src.buffer->setContentHash(view.src.buffer->computeContentHash()); + } -//! An interface for easy manipulation of meshes. -/** Scale, set alpha value, flip surfaces, and so on. This exists for -fixing problems with wrong imported or exported meshes quickly after -loading. It is not intended for doing mesh modifications and/or -animations during runtime. -*/ -class NBL_API2 IMeshManipulator : public virtual core::IReferenceCounted + // TODO: static inline IGeometryBase::SDataViewBase::SAABBStorage computeRange(const IGeometry::SDataView& view) + + // TODO: static inline void recomputeRange(IGeometry::SDataView& view) +}; + +//! An interface for easy manipulation of polygon geometries. +class NBL_API2 CPolygonGeometryManipulator { public: + static inline void recomputeContentHashes(ICPUPolygonGeometry* geo) + { + if (!geo) + return; + CGeometryManipulator::recomputeContentHash(geo->getPositionView()); + CGeometryManipulator::recomputeContentHash(geo->getIndexView()); + CGeometryManipulator::recomputeContentHash(geo->getNormalView()); + for (const auto& view : *geo->getJointWeightViews()) + { + CGeometryManipulator::recomputeContentHash(view.indices); + CGeometryManipulator::recomputeContentHash(view.weights); + } + if (auto pView=geo->getJointOBBView(); pView) + CGeometryManipulator::recomputeContentHash(*pView); + for (const auto& view : *geo->getAuxAttributeViews()) + CGeometryManipulator::recomputeContentHash(view); + } + + // TODO: recomputeRanges + //! Comparison methods enum E_ERROR_METRIC { @@ -50,6 +72,7 @@ class NBL_API2 IMeshManipulator : public virtual core::IReferenceCounted EEM_QUATERNION, EEM_COUNT }; +#if 0 // TODO: REDO //! Struct used to pass chosen comparison method and epsilon to functions performing error metrics. /** By default epsilon equals 2^-16 and EEM_POSITIONS comparison method is set. @@ -200,53 +223,6 @@ class NBL_API2 IMeshManipulator : public virtual core::IReferenceCounted */ static core::smart_refctd_ptr idxBufferFromTrianglesFanToTriangles(const void* _input, uint32_t& _idxCount, E_INDEX_TYPE _inIndexType, E_INDEX_TYPE _outIndexType); - //! Get amount of polygons in mesh buffer. - /** \param meshbuffer Input mesh buffer - \param Outputted Number of polygons in mesh buffer, if successful. - \return If successfully can provide information */ - template - static inline bool getPolyCount(uint32_t& outCount, const IMeshBuffer* meshbuffer) - { - outCount = 0; - if (!meshbuffer) - return false; - if (!meshbuffer->getPipeline()) - return false; - - const auto& assemblyParams = meshbuffer->getPipeline()->getCachedCreationParams().primitiveAssembly; - const E_PRIMITIVE_TOPOLOGY primType = assemblyParams.primitiveType; - switch (primType) - { - case EPT_POINT_LIST: - outCount = meshbuffer->getIndexCount(); - break; - case EPT_LINE_STRIP: - outCount = meshbuffer->getIndexCount() - 1; - break; - case EPT_LINE_LIST: - outCount = meshbuffer->getIndexCount() / 2; - break; - case EPT_TRIANGLE_STRIP: - outCount = meshbuffer->getIndexCount() - 2; - break; - case EPT_TRIANGLE_FAN: - outCount = meshbuffer->getIndexCount() - 2; - break; - case EPT_TRIANGLE_LIST: - outCount = meshbuffer->getIndexCount() / 3; - break; - case EPT_PATCH_LIST: - outCount = meshbuffer->getIndexCount() / assemblyParams.tessPatchVertCount; - break; - default: - assert(false); // need to implement calculation for more types - return false; - break; - } - - return true; - } - //! static inline std::array getTriangleIndices(const ICPUMeshBuffer* mb, uint32_t triangleIx) { @@ -620,77 +596,141 @@ class NBL_API2 IMeshManipulator : public virtual core::IReferenceCounted params.primitiveType = _newPrimitiveType; } } -#if 0 // TODO: Later - //! Orders meshbuffers according to a predicate - /** - @param _begin non-const iterator to beginning of meshbuffer range - @param _end non-const iterator to ending of meshbuffer range - */ - struct DefaultMeshBufferOrder +#endif +}; + +#if 0 + +//! An interface for easy manipulation of meshes. +/** Scale, set alpha value, flip surfaces, and so on. This exists for fixing +problems with wrong imported or exported meshes quickly after loading. It is +not intended for doing mesh modifications and/or animations during runtime. +*/ +class CMeshManipulator : public IMeshManipulator +{ + struct SAttrib { - public: - template - inline bool operator()(const T& lhs, const T& rhs) const - { - return false; - } + E_FORMAT type; + E_FORMAT prevType; + size_t size; + uint32_t vaid; + size_t offset; + + SAttrib() : type(EF_UNKNOWN), size(0), vaid(ICPUMeshBuffer::MAX_VERTEX_ATTRIB_COUNT) {} + + friend bool operator>(const SAttrib& _a, const SAttrib& _b) { return _a.size > _b.size; } }; - template - static inline void sortMeshBuffers(Iterator _begin, Iterator _end, mesh_buffer_order_t&& order=DefaultMeshBufferOrder()) + struct SAttribTypeChoice { - std::sort(_begin,_end,std::move(order)); - } -#endif - //! Get amount of polygons in mesh. - /** \param meshbuffer Input mesh - \param Outputted Number of polygons in mesh, if successful. - \return If successfully can provide information */ - template - static inline bool getPolyCount(uint32_t& outCount, const IMesh* mesh) + E_FORMAT type; + }; + + public: + static core::smart_refctd_ptr createMeshBufferFetchOptimized(const ICPUMeshBuffer* _inbuffer); + + CQuantNormalCache* getQuantNormalCache() override { return &quantNormalCache; } + CQuantQuaternionCache* getQuantQuaternionCache() override { return &quantQuaternionCache; } + + private: + friend class IMeshManipulator; + + template + static void _filterInvalidTriangles(ICPUMeshBuffer* _input); + + //! Meant to create 32bit index buffer from subrange of index buffer containing 16bit indices. Remember to set to index buffer offset to 0 after mapping buffer resulting from this function. + static inline core::smart_refctd_ptr create32BitFrom16BitIdxBufferSubrange(const uint16_t* _in, uint32_t _idxCount) { - outCount = 0u; - if (!mesh) - return false; + if (!_in) + return nullptr; - bool retval = true; - for (auto mb : mesh->getMeshBuffers()) - { - uint32_t trianglecount; - retval = retval && getPolyCount(trianglecount,mb); - outCount += trianglecount; - } + auto out = ICPUBuffer::create({ sizeof(uint32_t) * _idxCount }); + + auto* outPtr = reinterpret_cast(out->getPointer()); + + for (uint32_t i=0u; i<_idxCount; ++i) + outPtr[i] = _in[i]; - return retval; + return out; } - //! Calculates bounding box of the mesh - template - static inline core::aabbox3df calculateBoundingBox(const IMesh* mesh) + static core::vector findBetterFormatF(E_FORMAT* _outType, size_t* _outSize, E_FORMAT* _outPrevType, const ICPUMeshBuffer* _meshbuffer, uint32_t _attrId, const SErrorMetric& _errMetric, CQuantNormalCache& _cache); + + struct SIntegerAttr { - core::aabbox3df aabb(FLT_MAX, FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX); + uint32_t pointer[4]; + }; + static core::vector findBetterFormatI(E_FORMAT* _outType, size_t* _outSize, E_FORMAT* _outPrevType, const ICPUMeshBuffer* _meshbuffer, uint32_t _attrId, const SErrorMetric& _errMetric); + + //E_COMPONENT_TYPE getBestTypeF(bool _normalized, E_COMPONENTS_PER_ATTRIBUTE _cpa, size_t* _outSize, E_COMPONENTS_PER_ATTRIBUTE* _outCpa, const float* _min, const float* _max) const; + static E_FORMAT getBestTypeI(E_FORMAT _originalType, size_t* _outSize, const uint32_t* _min, const uint32_t* _max); + static core::vector findTypesOfProperRangeF(E_FORMAT _type, size_t _sizeThreshold, const float* _min, const float* _max, const SErrorMetric& _errMetric); - auto meshbuffers = mesh->getMeshBuffers(); - for (auto mesh : meshbuffers) - aabb.addInternalBox(mesh->getBoundingBox()); + //! Calculates quantization errors and compares them with given epsilon. + /** @returns false when first of calculated errors goes above epsilon or true if reached end without such. */ + static bool calcMaxQuantizationError(const SAttribTypeChoice& _srcType, const SAttribTypeChoice& _dstType, const core::vector& _data, const SErrorMetric& _errMetric, CQuantNormalCache& _cache); + + template + static inline core::smart_refctd_ptr lineStripsToLines(const void* _input, uint32_t& _idxCount) + { + const auto outputSize = _idxCount = (_idxCount - 1) * 2; - return aabb; + auto output = ICPUBuffer::create({ sizeof(OutType)*outputSize }); + const auto* iptr = reinterpret_cast(_input); + auto* optr = reinterpret_cast(output->getPointer()); + for (uint32_t i = 0, j = 0; i < outputSize;) + { + optr[i++] = iptr[j++]; + optr[i++] = iptr[j]; + } + return output; } - //! Recalculates the cached bounding box of the mesh - template - static inline void recalculateBoundingBox(IMesh* mesh) + template + static inline core::smart_refctd_ptr triangleStripsToTriangles(const void* _input, uint32_t& _idxCount) { - mesh->setBoundingBox(calculateBoundingBox(mesh)); + const auto outputSize = _idxCount = (_idxCount - 2) * 3; + + auto output = ICPUBuffer::create({ sizeof(OutType)*outputSize }); + const auto* iptr = reinterpret_cast(_input); + auto* optr = reinterpret_cast(output->getPointer()); + for (uint32_t i = 0, j = 0; i < outputSize; j += 2) + { + optr[i++] = iptr[j + 0]; + optr[i++] = iptr[j + 1]; + optr[i++] = iptr[j + 2]; + if (i == outputSize) + break; + optr[i++] = iptr[j + 2]; + optr[i++] = iptr[j + 1]; + optr[i++] = iptr[j + 3]; + } + return output; } + template + static inline core::smart_refctd_ptr trianglesFanToTriangles(const void* _input, uint32_t& _idxCount) + { + const auto outputSize = _idxCount = (_idxCount - 2) * 3; - //! - virtual CQuantNormalCache* getQuantNormalCache() = 0; - virtual CQuantQuaternionCache* getQuantQuaternionCache() = 0; -}; + auto output = ICPUBuffer::create({ sizeof(OutType)*outputSize }); + const auto* iptr = reinterpret_cast(_input); + auto* optr = reinterpret_cast(output->getPointer()); + for (uint32_t i = 0, j = 1; i < outputSize;) + { + optr[i++] = iptr[0]; + optr[i++] = iptr[j++]; + optr[i++] = iptr[j]; + } + return output; + } -} // end namespace scene -} // end namespace nbl + private: + CQuantNormalCache quantNormalCache; + CQuantQuaternionCache quantQuaternionCache; +}; +#endif +// TODO: Utility in another header for GeometryCollection to compute AABBs, deal with skins (joints), etc. +} // end namespace nbl::asset #endif diff --git a/include/nbl/asset/utils/CSPIRVIntrospector.h b/include/nbl/asset/utils/CSPIRVIntrospector.h index 0d7d678549..eea11652fc 100644 --- a/include/nbl/asset/utils/CSPIRVIntrospector.h +++ b/include/nbl/asset/utils/CSPIRVIntrospector.h @@ -15,7 +15,6 @@ #include "nbl/asset/IShader.h" #include "nbl/asset/ICPUImageView.h" #include "nbl/asset/ICPUComputePipeline.h" -#include "nbl/asset/ICPURenderpassIndependentPipeline.h" #include "nbl/asset/utils/CGLSLCompiler.h" #include "nbl/core/definitions.h" diff --git a/include/nbl/asset/utils/IMeshPacker.h b/include/nbl/asset/utils/IMeshPacker.h deleted file mode 100644 index 3f09062b18..0000000000 --- a/include/nbl/asset/utils/IMeshPacker.h +++ /dev/null @@ -1,636 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_I_MESH_PACKER_H_INCLUDED__ -#define __NBL_ASSET_I_MESH_PACKER_H_INCLUDED__ - -#include "nbl/asset/utils/IMeshManipulator.h" -#include "nbl/core/math/morton.h" - -namespace nbl -{ -namespace asset -{ - -class IMeshPackerBase : public virtual core::IReferenceCounted -{ - public: - constexpr static uint32_t MAX_TRIANGLES_IN_BATCH_CNT = 21845u; - - struct ReservedAllocationMeshBuffersBase - { - uint32_t mdiAllocationOffset; - uint32_t mdiAllocationReservedCnt; - uint32_t indexAllocationOffset; - uint32_t indexAllocationReservedCnt; - - inline bool isValid() - { - return this->mdiAllocationOffset!=core::GeneralpurposeAddressAllocator::invalid_address; - } - }; - struct PackedMeshBufferData - { - uint32_t mdiParameterOffset; // add to `CCPUMeshPacker::getMultiDrawIndirectBuffer()->getPointer() to get `DrawElementsIndirectCommand_t` address - uint32_t mdiParameterCount; - - inline bool isValid() - { - return this->mdiParameterOffset != core::GeneralpurposeAddressAllocator::invalid_address; - } - }; - - inline uint16_t getMinTriangleCountPerMDI() const { return m_minTriangleCountPerMDIData; } - inline uint16_t getMaxTriangleCountPerMDI() const { return m_maxTriangleCountPerMDIData; } - - protected: - using alctrTraits = core::address_allocator_traits>; - - IMeshPackerBase(uint16_t minTriangleCountPerMDIData, uint16_t maxTriangleCountPerMDIData) - :m_maxTriangleCountPerMDIData(maxTriangleCountPerMDIData), - m_minTriangleCountPerMDIData(minTriangleCountPerMDIData) - { - assert(minTriangleCountPerMDIData <= MAX_TRIANGLES_IN_BATCH_CNT); - assert(maxTriangleCountPerMDIData <= MAX_TRIANGLES_IN_BATCH_CNT); - assert(minTriangleCountPerMDIData <= maxTriangleCountPerMDIData); - assert(minTriangleCountPerMDIData > 0u); - assert(maxTriangleCountPerMDIData > 0u); - }; - - virtual ~IMeshPackerBase() - { - _NBL_ALIGNED_FREE(const_cast(alctrTraits::getReservedSpacePtr(m_MDIDataAlctr))); - _NBL_ALIGNED_FREE(const_cast(alctrTraits::getReservedSpacePtr(m_idxBuffAlctr))); - _NBL_ALIGNED_FREE(const_cast(alctrTraits::getReservedSpacePtr(m_vtxBuffAlctr))); - } - - struct AllocationParamsCommon - { - // Maximum number of 16 bit indicies that may be allocated - size_t indexBuffSupportedCnt = 67108864ull; /* 128MB*/ - - /* Maximum byte size for vertex data allocation - For `CCPUMeshPackerV1` this will be maximum byte size of buffer containing only attributes with EVIR_PER_VERTEX input rate. - For `CCPUMeshPackerV2` this will be maximum byte size of buffer containing attributes with both EVIR_PER_VERTEX and EVIR_PER_INSTANCE input rate. - */ - size_t vertexBuffSupportedByteSize = 134217728ull; /* 128MB*/ - - // Maximum number of MDI structs that may be allocated - size_t MDIDataBuffSupportedCnt = 16777216ull; /* 16MB assuming MDIStructType is DrawElementsIndirectCommand_t*/ - - // Minimum count of 16 bit indicies allocated per allocation - size_t indexBufferMinAllocCnt = 256ull; - - // Minimum bytes of vertex data allocated per allocation - size_t vertexBufferMinAllocByteSize = 32ull; - - // Minimum count of MDI structs allocated per allocation - size_t MDIDataBuffMinAllocCnt = 32ull; - }; - - void initializeCommonAllocators(const AllocationParamsCommon& allocParams) - { - if (allocParams.indexBuffSupportedCnt) - { - - void* resSpcTmp = _NBL_ALIGNED_MALLOC(core::GeneralpurposeAddressAllocator::reserved_size(alignof(uint16_t), allocParams.indexBuffSupportedCnt, allocParams.indexBufferMinAllocCnt), _NBL_SIMD_ALIGNMENT); - assert(resSpcTmp != nullptr); - m_idxBuffAlctr = core::GeneralpurposeAddressAllocator(resSpcTmp, 0u, 0u, alignof(uint16_t), allocParams.indexBuffSupportedCnt, allocParams.indexBufferMinAllocCnt); - } - - if (allocParams.vertexBuffSupportedByteSize) - { - void* resSpcTmp = _NBL_ALIGNED_MALLOC(core::GeneralpurposeAddressAllocator::reserved_size(32u, allocParams.vertexBuffSupportedByteSize, allocParams.vertexBufferMinAllocByteSize), _NBL_SIMD_ALIGNMENT); - assert(resSpcTmp != nullptr); - m_vtxBuffAlctr = core::GeneralpurposeAddressAllocator(resSpcTmp, 0u, 0u, 32u, allocParams.vertexBuffSupportedByteSize, allocParams.vertexBufferMinAllocByteSize); - } - - if (allocParams.MDIDataBuffSupportedCnt) - { - void* resSpcTmp = _NBL_ALIGNED_MALLOC(core::GeneralpurposeAddressAllocator::reserved_size(alignof(std::max_align_t), allocParams.MDIDataBuffSupportedCnt, allocParams.MDIDataBuffMinAllocCnt), _NBL_SIMD_ALIGNMENT); - assert(resSpcTmp != nullptr); - m_MDIDataAlctr = core::GeneralpurposeAddressAllocator(resSpcTmp, 0u, 0u, alignof(std::max_align_t), allocParams.MDIDataBuffSupportedCnt, allocParams.MDIDataBuffMinAllocCnt); - } - } - - void initializeCommonAllocators( - const core::GeneralpurposeAddressAllocator& mdiAlctr, - const core::GeneralpurposeAddressAllocator& idxAlctr, - const core::GeneralpurposeAddressAllocator& vtxAlctr - ) - { - uint32_t alctrBuffSz = alctrTraits::get_total_size(mdiAlctr); - void* resSpcTmp = _NBL_ALIGNED_MALLOC(alctrTraits::reserved_size(alctrBuffSz, mdiAlctr), _NBL_SIMD_ALIGNMENT); - m_MDIDataAlctr = core::GeneralpurposeAddressAllocator(alctrBuffSz, mdiAlctr, resSpcTmp); - - alctrBuffSz = alctrTraits::get_total_size(idxAlctr); - resSpcTmp = _NBL_ALIGNED_MALLOC(alctrTraits::reserved_size(alctrBuffSz, idxAlctr), _NBL_SIMD_ALIGNMENT); - m_idxBuffAlctr = core::GeneralpurposeAddressAllocator(alctrBuffSz, idxAlctr, resSpcTmp); - - alctrBuffSz = alctrTraits::get_total_size(vtxAlctr); - resSpcTmp = _NBL_ALIGNED_MALLOC(alctrTraits::reserved_size(alctrBuffSz, vtxAlctr), _NBL_SIMD_ALIGNMENT); - m_vtxBuffAlctr = core::GeneralpurposeAddressAllocator(alctrBuffSz, vtxAlctr, resSpcTmp); - } - - void free(const ReservedAllocationMeshBuffersBase& rambb) - { - if (rambb.indexAllocationOffset != INVALID_ADDRESS) - m_idxBuffAlctr.free_addr(rambb.indexAllocationOffset,rambb.indexAllocationReservedCnt); - - if (rambb.mdiAllocationOffset != INVALID_ADDRESS) - m_MDIDataAlctr.free_addr(rambb.mdiAllocationOffset,rambb.mdiAllocationReservedCnt); - } - - // - _NBL_STATIC_INLINE_CONSTEXPR uint32_t INVALID_ADDRESS = core::GeneralpurposeAddressAllocator::invalid_address; - - core::GeneralpurposeAddressAllocator m_vtxBuffAlctr; - core::GeneralpurposeAddressAllocator m_idxBuffAlctr; - core::GeneralpurposeAddressAllocator m_MDIDataAlctr; - - const uint16_t m_minTriangleCountPerMDIData; - const uint16_t m_maxTriangleCountPerMDIData; - -}; - -#if 0 // REWRITE -template -class IMeshPacker : public IMeshPackerBase -{ - static_assert(std::is_base_of::value); - -public: - /* - @param minTriangleCountPerMDIData must be <= 21845 - @param maxTriangleCountPerMDIData must be <= 21845 - */ - IMeshPacker(uint16_t minTriangleCountPerMDIData, uint16_t maxTriangleCountPerMDIData) - :IMeshPackerBase(minTriangleCountPerMDIData, maxTriangleCountPerMDIData) - { - } - - //! shrinks byte size of all output buffers, so they are large enough to fit currently allocated contents. Call this function before `instantiateDataStorage` - virtual void shrinkOutputBuffersSize() - { - uint32_t mdiDataBuffNewSize = m_MDIDataAlctr.safe_shrink_size(0u, alctrTraits::max_alignment(m_MDIDataAlctr)); - uint32_t idxBuffNewSize = m_idxBuffAlctr.safe_shrink_size(0u, alctrTraits::max_alignment(m_idxBuffAlctr)); - uint32_t vtxBuffNewSize = m_vtxBuffAlctr.safe_shrink_size(0u, alctrTraits::max_alignment(m_vtxBuffAlctr)); - - const void* oldReserved = alctrTraits::getReservedSpacePtr(m_MDIDataAlctr); - m_MDIDataAlctr = core::GeneralpurposeAddressAllocator(mdiDataBuffNewSize, std::move(m_MDIDataAlctr), _NBL_ALIGNED_MALLOC(alctrTraits::reserved_size(mdiDataBuffNewSize, m_MDIDataAlctr), _NBL_SIMD_ALIGNMENT)); - _NBL_ALIGNED_FREE(const_cast(oldReserved)); - - oldReserved = alctrTraits::getReservedSpacePtr(m_idxBuffAlctr); - m_idxBuffAlctr = core::GeneralpurposeAddressAllocator(idxBuffNewSize, std::move(m_idxBuffAlctr), _NBL_ALIGNED_MALLOC(alctrTraits::reserved_size(idxBuffNewSize, m_idxBuffAlctr), _NBL_SIMD_ALIGNMENT)); - _NBL_ALIGNED_FREE(const_cast(oldReserved)); - - oldReserved = alctrTraits::getReservedSpacePtr(m_vtxBuffAlctr); - m_vtxBuffAlctr = core::GeneralpurposeAddressAllocator(vtxBuffNewSize, std::move(m_vtxBuffAlctr), _NBL_ALIGNED_MALLOC(alctrTraits::reserved_size(vtxBuffNewSize, m_vtxBuffAlctr), _NBL_SIMD_ALIGNMENT)); - _NBL_ALIGNED_FREE(const_cast(oldReserved)); - } - - //! Returns maximum number of mdi structs needed to draw range of mesh buffers described by range mbBegin .. mbEnd, actual number of MDI structs needed may differ - template - uint32_t calcMDIStructMaxCount(const MeshBufferIterator mbBegin, const MeshBufferIterator mbEnd) - { - uint32_t acc = 0u; - for (auto mbIt = mbBegin; mbIt != mbEnd; mbIt++) - { - auto mb = *mbIt; - const size_t idxCnt = calcIdxCntAfterConversionToTriangleList(mb); - const uint32_t triCnt = idxCnt / 3; - assert(idxCnt % 3 == 0); - - acc += calcBatchCountBound(triCnt); - } - - return acc; - } - -protected: - virtual ~IMeshPacker() {} - - static inline size_t calcVertexSize(const SVertexInputParams& vtxInputParams, const E_VERTEX_INPUT_RATE inputRate) - { - size_t size = 0ull; - for (size_t i = 0; i < SVertexInputParams::MAX_VERTEX_ATTRIB_COUNT; ++i) - { - if (vtxInputParams.enabledAttribFlags & (1u << i)) - if(vtxInputParams.bindings[i].inputRate == inputRate) - size += asset::getTexelOrBlockBytesize(static_cast(vtxInputParams.attributes[i].format)); - } - - return size; - } - - static inline uint32_t calcVertexCountBoundWithBatchDuplication(const MeshBufferType* meshBuffer) - { - uint32_t triCnt; - if (IMeshManipulator::getPolyCount(triCnt,meshBuffer)) - return triCnt * 3u; - return 0u; - } - - inline uint32_t calcBatchCountBound(uint32_t triCnt) const - { - if (triCnt!=0u) - return (triCnt-1u)/m_minTriangleCountPerMDIData+1u; - return 0u; - } - - struct Triangle - { - uint32_t oldIndices[3]; - }; - - struct TriangleBatches - { - TriangleBatches(uint32_t triCnt) - { - triangles = core::vector(triCnt); - } - - core::vector triangles; - core::vector ranges; - }; - - struct IdxBufferParams - { - SBufferBinding idxBuffer = { 0u, nullptr }; - E_INDEX_TYPE idxType = EIT_UNKNOWN; - }; - - //TODO: functions: constructTriangleBatches, convertIdxBufferToTriangles, deinterleaveAndCopyAttribute and deinterleaveAndCopyPerInstanceAttribute - //will not work with IGPUMeshBuffer as MeshBufferType, move it to new `ICPUMeshPacker` - - TriangleBatches constructTriangleBatches(const MeshBufferType* meshBuffer, IdxBufferParams idxBufferParams, core::aabbox3df*& aabbs) const - { - uint32_t triCnt; - const bool success = IMeshManipulator::getPolyCount(triCnt,meshBuffer); - assert(success); - - const uint32_t batchCnt = calcBatchCountBound(triCnt); - assert(batchCnt != 0u); - - struct MortonTriangle - { - MortonTriangle() = default; - - MortonTriangle(uint16_t fixedPointPos[3], float area) - { - auto tmp = reinterpret_cast(key); - std::copy_n(fixedPointPos,3u,tmp); - tmp[3] = core::Float16Compressor::compress(area); - } - - void complete(float maxArea) - { - auto tmp = reinterpret_cast(key); - const float area = core::Float16Compressor::decompress(tmp[3]); - const float scale = 0.5f; // square root - uint16_t logRelArea = uint16_t(65535.5f+core::clamp(scale*std::log2f(area/maxArea),-65535.5f,0.f)); - key = core::morton4d_encode(tmp[0],tmp[1],tmp[2],logRelArea); - } - - uint64_t key; - }; - - //TODO: use SoA instead (with core::radix_sort): - //core::vector triangles; - //core::vector triangleMortonCodes; - //where `triangles` is member of `TriangleBatch` struct - struct TriangleMortonCodePair - { - Triangle triangle; - MortonTriangle mortonCode; - - inline bool operator<(const TriangleMortonCodePair& other) - { - return this->mortonCode.key < other.mortonCode.key; - } - }; - - TriangleBatches triangleBatches(triCnt); - core::vector triangles(triCnt); //#1 - - core::smart_refctd_ptr mbTmp = core::smart_refctd_ptr_static_cast(meshBuffer->clone()); - mbTmp->setIndexBufferBinding(std::move(idxBufferParams.idxBuffer)); - mbTmp->setIndexType(idxBufferParams.idxType); - mbTmp->getPipeline()->getPrimitiveAssemblyParams().primitiveType = EPT_TRIANGLE_LIST; - - //triangle reordering - { - const core::aabbox3df aabb = IMeshManipulator::calculateBoundingBox(mbTmp.get()); - - uint32_t ix = 0u; - float maxTriangleArea = 0.0f; - for (auto it = triangles.begin(); it != triangles.end(); it++) - { - auto triangleIndices = IMeshManipulator::getTriangleIndices(mbTmp.get(), ix++); - //have to copy there - std::copy(triangleIndices.begin(), triangleIndices.end(), it->triangle.oldIndices); - - core::vectorSIMDf trianglePos[3]; - trianglePos[0] = mbTmp->getPosition(it->triangle.oldIndices[0]); - trianglePos[1] = mbTmp->getPosition(it->triangle.oldIndices[1]); - trianglePos[2] = mbTmp->getPosition(it->triangle.oldIndices[2]); - - const core::vectorSIMDf centroid = ((trianglePos[0] + trianglePos[1] + trianglePos[2]) / 3.0f) - core::vectorSIMDf(aabb.MinEdge.X, aabb.MinEdge.Y, aabb.MinEdge.Z); - uint16_t fixedPointPos[3]; - fixedPointPos[0] = uint16_t(centroid.x * 65535.5f / aabb.getExtent().X); - fixedPointPos[1] = uint16_t(centroid.y * 65535.5f / aabb.getExtent().Y); - fixedPointPos[2] = uint16_t(centroid.z * 65535.5f / aabb.getExtent().Z); - - float area = core::cross(trianglePos[1] - trianglePos[0], trianglePos[2] - trianglePos[0]).x; - it->mortonCode = MortonTriangle(fixedPointPos, area); - - if (area > maxTriangleArea) - maxTriangleArea = area; - } - - //complete morton code - for (auto it = triangles.begin(); it != triangles.end(); it++) - it->mortonCode.complete(maxTriangleArea); - - std::sort(triangles.begin(), triangles.end()); - } - - //copying, after radix_sort this will be removed - //TODO durning radix_sort integration: - //since there will be distinct arrays for triangles and their morton code use `triangleBatches.triangles` instead of #1 - for (uint32_t i = 0u; i < triCnt; i++) - triangleBatches.triangles[i] = triangles[i].triangle; - - //set ranges - Triangle* triangleArrayBegin = triangleBatches.triangles.data(); - Triangle* triangleArrayEnd = triangleArrayBegin + triangleBatches.triangles.size(); - const uint32_t triangleCnt = triangleBatches.triangles.size(); - - //aabb batch division - { - triangleBatches.ranges.push_back(triangleArrayBegin); - for (auto nextTriangle = triangleArrayBegin; nextTriangle < triangleArrayEnd; ) - { - const Triangle* batchBegin = *(triangleBatches.ranges.end() - 1u); - const Triangle* batchEnd = batchBegin + m_minTriangleCountPerMDIData; - - //find min and max edge - core::vector3df_SIMD min(std::numeric_limits::max()); - core::vector3df_SIMD max(-std::numeric_limits::max()); - - auto extendAABB = [&min, &max, &meshBuffer](auto triangleIt) -> void - { - for (uint32_t i = 0u; i < 3u; i++) - { - auto vxPos = meshBuffer->getPosition(triangleIt->oldIndices[i]); - min = core::min(vxPos, min); - max = core::max(vxPos, max); - } - }; - - for (uint32_t i = 0u; i < m_minTriangleCountPerMDIData && nextTriangle != triangleArrayEnd; i++) - extendAABB(nextTriangle++); - - auto halfAreaAABB = [&min, &max]() -> float - { - auto extent = max - min; - return extent.x * extent.y + extent.x * extent.z + extent.y * extent.z; - }; - - constexpr float kGrowthLimit = 1.025f; - float batchArea = halfAreaAABB(); - for (uint16_t i = m_minTriangleCountPerMDIData; nextTriangle != triangleArrayEnd && i < m_maxTriangleCountPerMDIData; i++) - { - if(aabbs) - *aabbs = core::aabbox3df(core::vector3df(min.x, min.y, min.z), core::vector3df(max.x, max.y, max.z)); - - extendAABB(nextTriangle); - float newBatchArea = halfAreaAABB(); - if (newBatchArea > kGrowthLimit* batchArea) - break; - nextTriangle++; - batchArea = newBatchArea; - } - - if (aabbs) - { - if (nextTriangle == triangleArrayEnd || m_minTriangleCountPerMDIData == m_maxTriangleCountPerMDIData) - *aabbs = core::aabbox3df(core::vector3df(min.x, min.y, min.z), core::vector3df(max.x, max.y, max.z)); - aabbs++; - } - - triangleBatches.ranges.push_back(nextTriangle); - } - - } - - return triangleBatches; - } - - static core::unordered_map constructNewIndicesFromTriangleBatchAndUpdateUnifiedIndexBuffer(TriangleBatches& batches, uint32_t batchIdx, uint16_t*& indexBuffPtr) - { - core::unordered_map usedVertices; - core::vector newIdxTris = batches.triangles; - - auto batchBegin = batches.ranges[batchIdx]; - auto batchEnd = batches.ranges[batchIdx + 1]; - - const uint32_t triangleInBatchCnt = std::distance(batchBegin, batchEnd); - const uint32_t idxInBatchCnt = 3u * triangleInBatchCnt; - - uint32_t newIdx = 0u; - for (uint32_t i = 0u; i < triangleInBatchCnt; i++) - { - const Triangle* const triangle = batchBegin + i; - for (int32_t j = 0; j < 3; j++) - { - const uint32_t oldIndex = triangle->oldIndices[j]; - auto result = usedVertices.insert(std::make_pair(oldIndex, newIdx)); - - newIdxTris[i].oldIndices[j] = result.second ? newIdx++ : result.first->second; - } - } - - //TODO: cache optimization - //copy indices into unified index buffer - for (size_t i = 0; i < triangleInBatchCnt; i++) - { - for (int j = 0; j < 3; j++) - { - *indexBuffPtr = newIdxTris[i].oldIndices[j]; - indexBuffPtr++; - } - } - - return usedVertices; - } - - static void deinterleaveAndCopyAttribute(MeshBufferType* meshBuffer, uint16_t attrLocation, const core::unordered_map& usedVertices, uint8_t* dstAttrPtr) - { - const uint8_t* const srcAttrPtr = meshBuffer->getAttribPointer(attrLocation); - SVertexInputParams& mbVtxInputParams = meshBuffer->getPipeline()->getVertexInputParams(); - SVertexInputAttribParams MBAttrib = mbVtxInputParams.attributes[attrLocation]; - SVertexInputBindingParams attribBinding = mbVtxInputParams.bindings[MBAttrib.binding]; - const size_t attrSize = asset::getTexelOrBlockBytesize(static_cast(MBAttrib.format)); - const size_t stride = (attribBinding.stride) == 0 ? attrSize : attribBinding.stride; - - for (auto index : usedVertices) - { - const uint8_t* attrSrc = srcAttrPtr + (index.first * stride); - uint8_t* attrDest = dstAttrPtr + (index.second * attrSize); - memcpy(attrDest, attrSrc, attrSize); - } - } - - static void deinterleaveAndCopyPerInstanceAttribute(MeshBufferType* meshBuffer, uint16_t attrLocation, uint8_t* dstAttrPtr) - { - const uint8_t* const srcAttrPtr = meshBuffer->getAttribPointer(attrLocation); - SVertexInputParams& mbVtxInputParams = meshBuffer->getPipeline()->getVertexInputParams(); - SVertexInputAttribParams MBAttrib = mbVtxInputParams.attributes[attrLocation]; - SVertexInputBindingParams attribBinding = mbVtxInputParams.bindings[MBAttrib.binding]; - const size_t attrSize = asset::getTexelOrBlockBytesize(static_cast(MBAttrib.format)); - const size_t stride = (attribBinding.stride) == 0 ? attrSize : attribBinding.stride; - - const uint32_t insCnt = meshBuffer->getInstanceCount(); - for (uint32_t i = 0u; i < insCnt; i++) - { - const uint8_t* attrSrc = srcAttrPtr + (i * stride); - uint8_t* attrDest = dstAttrPtr + (i * attrSize); - memcpy(attrDest, attrSrc, attrSize); - } - } - - inline uint32_t calcIdxCntAfterConversionToTriangleList(const MeshBufferType* meshBuffer) - { - const auto& params = meshBuffer->getPipeline()->getPrimitiveAssemblyParams(); - - switch (params.primitiveType) - { - case EPT_TRIANGLE_LIST: - case EPT_TRIANGLE_STRIP: - case EPT_TRIANGLE_FAN: - break; - case EPT_POINT_LIST: - case EPT_LINE_LIST: - case EPT_LINE_STRIP: - case EPT_LINE_LIST_WITH_ADJACENCY: - case EPT_LINE_STRIP_WITH_ADJACENCY: - case EPT_TRIANGLE_LIST_WITH_ADJACENCY: - case EPT_TRIANGLE_STRIP_WITH_ADJACENCY: - case EPT_PATCH_LIST: - default: - assert(false); - break; - } - - uint32_t triCnt; - const bool success = IMeshManipulator::getPolyCount(triCnt, meshBuffer); - assert(success); - - return triCnt * 3; - } - inline uint32_t calcIdxCntAfterConversionToTriangleList(const core::smart_refctd_ptr& meshBuffer) - { - return calcIdxCntAfterConversionToTriangleList(meshBuffer.get()); - } - inline uint32_t calcIdxCntAfterConversionToTriangleList(const core::smart_refctd_ptr& meshBuffer) - { - return calcIdxCntAfterConversionToTriangleList(meshBuffer.get()); - } - - std::pair> convertIdxBufferToTriangles(MeshBufferType* meshBuffer) - { - const auto mbIdxBuffer = meshBuffer->getIndexBufferBinding().buffer; - E_INDEX_TYPE idxType = meshBuffer->getIndexType(); - const uint32_t idxCount = meshBuffer->getIndexCount(); - if (idxCount == 0) - return { 0u, nullptr }; - - const bool iota = idxType == EIT_UNKNOWN || !mbIdxBuffer; - core::smart_refctd_ptr idxBufferToProcess; - if (iota) - { - idxBufferToProcess = core::make_smart_refctd_ptr(sizeof(uint32_t) * idxCount); - auto ptr = reinterpret_cast(idxBufferToProcess->getPointer()); - std::iota(ptr, ptr + idxCount, 0u); - idxType = EIT_32BIT; - } - else - { - idxBufferToProcess = mbIdxBuffer; - } - - std::pair> output; - output.first = meshBuffer->getIndexCount(); - - const auto& params = meshBuffer->getPipeline()->getPrimitiveAssemblyParams(); - switch (params.primitiveType) - { - case EPT_TRIANGLE_STRIP: - output.second = IMeshManipulator::idxBufferFromTriangleStripsToTriangles(idxBufferToProcess->getPointer(), output.first, idxType, idxType); - return output; - - case EPT_TRIANGLE_FAN: - output.second = IMeshManipulator::idxBufferFromTrianglesFanToTriangles(idxBufferToProcess->getPointer(), output.first, idxType, idxType); - return output; - - //TODO: packer should return when there is mesh buffer with one of following: - case EPT_TRIANGLE_LIST: - case EPT_POINT_LIST: - case EPT_LINE_LIST: - case EPT_LINE_STRIP: - case EPT_LINE_LIST_WITH_ADJACENCY: - case EPT_LINE_STRIP_WITH_ADJACENCY: - case EPT_TRIANGLE_LIST_WITH_ADJACENCY: - case EPT_TRIANGLE_STRIP_WITH_ADJACENCY: - case EPT_PATCH_LIST: - default: - assert(false); - return { 0u, nullptr }; - } - } - - IdxBufferParams createNewIdxBufferParamsForNonTriangleListTopologies(MeshBufferType* meshBuffer) - { - IdxBufferParams output; - - const auto& mbPrimitiveType = meshBuffer->getPipeline()->getPrimitiveAssemblyParams().primitiveType; - if (mbPrimitiveType == EPT_TRIANGLE_LIST) - { - const auto& mbIdxBuff = meshBuffer->getIndexBufferBinding(); - output.idxBuffer.offset = mbIdxBuff.offset; - output.idxBuffer.buffer = core::smart_refctd_ptr(mbIdxBuff.buffer); - output.idxType = meshBuffer->getIndexType(); - } - else - { - auto newIdxBuffer = convertIdxBufferToTriangles(meshBuffer); - output.idxBuffer.offset = 0u; - output.idxBuffer.buffer = newIdxBuffer.second; - output.idxType = EIT_32BIT; - } - - return output; - } - -protected: - template - struct PackerDataStoreCommon - { - static_assert(std::is_base_of::value); - - core::smart_refctd_ptr MDIDataBuffer; - - inline bool isValid() - { - return this->MDIDataBuffer->getPointer() != nullptr; - } - }; - -}; -#endif -} -} - -#endif \ No newline at end of file diff --git a/include/nbl/asset/utils/IMeshPackerV2.h b/include/nbl/asset/utils/IMeshPackerV2.h deleted file mode 100644 index 89aec7e685..0000000000 --- a/include/nbl/asset/utils/IMeshPackerV2.h +++ /dev/null @@ -1,759 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_I_MESH_PACKER_V2_H_INCLUDED__ -#define __NBL_ASSET_I_MESH_PACKER_V2_H_INCLUDED__ - -#include - -namespace nbl -{ -namespace asset -{ - -class IMeshPackerV2Base -{ -public: - class SupportedFormatsContainer - { - public: - template - void insertFormatsFromMeshBufferRange(MeshBufferIt mbBegin, MeshBufferIt mbEnd) - { - for (auto it = mbBegin; it != mbEnd; it++) - { - const auto& mbVtxInputParams = (*it)->getPipeline()->getVertexInputParams(); - for (uint16_t attrBit = 0x0001, location = 0; location < SVertexInputParams::MAX_ATTR_BUF_BINDING_COUNT; attrBit <<= 1, location++) - { - if (!(attrBit & mbVtxInputParams.enabledAttribFlags)) - continue; - - formats.insert(static_cast(mbVtxInputParams.attributes[location].format)); - } - } - } - - inline void insert(E_FORMAT format) - { - formats.insert(format); - } - - inline const core::unordered_set& getFormats() const { return formats; } - - private: - core::unordered_set formats; - }; - -public: - enum E_UTB_ARRAY_TYPE : uint8_t - { - EUAT_FLOAT, - EUAT_INT, - EUAT_UINT, - EUAT_UNKNOWN - }; - struct VirtualAttribConfig - { - VirtualAttribConfig() = default; - - VirtualAttribConfig(const SupportedFormatsContainer& formats) - { - const auto& formatsSet = formats.getFormats(); - for (auto it = formatsSet.begin(); it != formatsSet.end(); it++) - insertAttribFormat(*it); - } - - VirtualAttribConfig(const VirtualAttribConfig& other) - { - std::copy_n(other.utbs, EUAT_UNKNOWN, utbs); - - isUintBufferUsed = other.isUintBufferUsed; - isUvec2BufferUsed = other.isUvec2BufferUsed; - isUvec3BufferUsed = other.isUvec3BufferUsed; - isUvec4BufferUsed = other.isUvec4BufferUsed; - } - - VirtualAttribConfig(VirtualAttribConfig&& other) - { - for (auto i = 0u; i < EUAT_UNKNOWN; i++) - utbs[i] = std::move(other.utbs[i]); - - isUintBufferUsed = other.isUintBufferUsed; - isUvec2BufferUsed = other.isUvec2BufferUsed; - isUvec3BufferUsed = other.isUvec3BufferUsed; - isUvec4BufferUsed = other.isUvec4BufferUsed; - - //other.utbs->clear(); - other.isUintBufferUsed = false; - other.isUvec2BufferUsed = false; - other.isUvec3BufferUsed = false; - other.isUvec4BufferUsed = false; - } - - core::unordered_map utbs[EUAT_UNKNOWN]; - bool isUintBufferUsed = false; - bool isUvec2BufferUsed = false; - bool isUvec3BufferUsed = false; - bool isUvec4BufferUsed = false; - - VirtualAttribConfig& operator=(const VirtualAttribConfig& other) - { - std::copy_n(other.utbs,EUAT_UNKNOWN,utbs); - - isUintBufferUsed = other.isUintBufferUsed; - isUvec2BufferUsed = other.isUvec2BufferUsed; - isUvec3BufferUsed = other.isUvec3BufferUsed; - isUvec4BufferUsed = other.isUvec4BufferUsed; - - return *this; - } - - VirtualAttribConfig& operator=(VirtualAttribConfig&& other) - { - for (auto i=0u; i -class IMeshPackerV2 : public IMeshPacker, public IMeshPackerV2Base -{ - static_assert(std::is_base_of::value); - - using AllocationParams = IMeshPackerBase::AllocationParamsCommon; - - using DescriptorSetLayoutType = typename DescriptorSetType::layout_t; -public: - using base_t = IMeshPacker; - struct AttribAllocParams - { - size_t offset = base_t::INVALID_ADDRESS; - size_t size = 0ull; - }; - - //TODO: REDESIGN - //mdi allocation offset and index allocation offset should be shared - struct ReservedAllocationMeshBuffers : ReservedAllocationMeshBuffersBase - { - AttribAllocParams attribAllocParams[SVertexInputParams::MAX_VERTEX_ATTRIB_COUNT]; - }; - struct VirtualAttribute - { - VirtualAttribute() : va(0u) {}; - - VirtualAttribute(uint16_t arrayElement, uint32_t offset) - :va(0u) - { - assert((offset & 0xF0000000u) == 0u); - - va |= static_cast(arrayElement) << 28u; - va |= offset; - } - - inline uint32_t getArrayElement() const { return core::bitfieldExtract(va,28,4); } - inline void setArrayElement(uint16_t arrayElement) { va = core::bitfieldInsert(va,arrayElement,28,4); } - - inline uint32_t getOffset() const { return core::bitfieldExtract(va,0,28); } - inline void setOffset(uint32_t offset) { va = core::bitfieldInsert(va,offset,0,28); } - - private: - uint32_t va; - }; - - struct CombinedDataOffsetTable - { - VirtualAttribute attribInfo[SVertexInputParams::MAX_VERTEX_ATTRIB_COUNT]; - }; - - struct PackerDataStore : base_t::template PackerDataStoreCommon - { - core::smart_refctd_ptr vertexBuffer; - core::smart_refctd_ptr indexBuffer; - }; - - inline uint32_t getFloatBufferBindingsCnt() const { return m_virtualAttribConfig.utbs[EUAT_FLOAT].size(); } - inline uint32_t getIntBufferBindingsCnt() const { return m_virtualAttribConfig.utbs[EUAT_INT].size(); } - // not including the UTB for the index buffer - inline uint32_t getUintBufferBindingsCnt() const { return m_virtualAttribConfig.utbs[EUAT_UINT].size(); } - - // the following cannot be called before 'instantiateDataStorage' - struct DSLayoutParamsUTB - { - uint32_t usamplersBinding = 0u; - uint32_t fsamplersBinding = 1u; - uint32_t isamplersBinding = 2u; - }; - std::string getGLSLForUTB(uint32_t descriptorSet = 0u, const DSLayoutParamsUTB& params = {}) - { - std::string result = "#define _NBL_VG_DESCRIPTOR_SET " + std::to_string(descriptorSet) + '\n'; - - result += "#define _NBL_VG_UINT_BUFFERS\n"; - result += "#define _NBL_VG_UINT_BUFFERS_BINDING " + std::to_string(params.usamplersBinding) + '\n'; - result += "#define _NBL_VG_UINT_BUFFERS_COUNT " + std::to_string(1u+getUintBufferBindingsCnt()) + '\n'; - if (getFloatBufferBindingsCnt()) - { - result += "#define _NBL_VG_FLOAT_BUFFERS\n"; - result += "#define _NBL_VG_FLOAT_BUFFERS_BINDING " + std::to_string(params.fsamplersBinding) + '\n'; - result += "#define _NBL_VG_FLOAT_BUFFERS_COUNT " + std::to_string(getFloatBufferBindingsCnt()) + '\n'; - } - if (getIntBufferBindingsCnt()) - { - result += "#define _NBL_VG_INT_BUFFERS\n"; - result += "#define _NBL_VG_INT_BUFFERS_BINDING " + std::to_string(params.isamplersBinding) + '\n'; - result += "#define _NBL_VG_INT_BUFFERS_COUNT " + std::to_string(getFloatBufferBindingsCnt()) + '\n'; - } - - return result; - } - - inline uint32_t getDSlayoutBindingsForUTB(typename DescriptorSetLayoutType::SBinding* outBindings, const DSLayoutParamsUTB& params = {}) const - { - const uint32_t bindingCount = 1u + // for the always present uint index buffer - (getFloatBufferBindingsCnt() ? 1u : 0u) + - (getIntBufferBindingsCnt() ? 1u : 0u); - - if (outBindings) - { - auto* bnd = outBindings; - auto fillBinding = [&bnd](uint32_t binding, uint32_t count) - { - bnd->binding = binding; - bnd->count = count; - bnd->stageFlags = asset::ISpecializedShader::ESS_ALL; - bnd->type = asset::IDescriptor::E_TYPE::ET_UNIFORM_TEXEL_BUFFER; - bnd->samplers = nullptr; - bnd++; - }; - - fillBinding(params.usamplersBinding,getUintBufferBindingsCnt()+1u); - if (getFloatBufferBindingsCnt()) - fillBinding(params.fsamplersBinding,getFloatBufferBindingsCnt()); - if (getIntBufferBindingsCnt()) - fillBinding(params.isamplersBinding,getIntBufferBindingsCnt()); - } - return bindingCount; - } - - inline std::pair getDescriptorSetWritesForUTB( - typename DescriptorSetType::SWriteDescriptorSet* outWrites, typename DescriptorSetType::SDescriptorInfo* outInfo, DescriptorSetType* dstSet, - std::function(core::smart_refctd_ptr&&,E_FORMAT)> createBufferView, const DSLayoutParamsUTB& params = {} - ) const - { - const uint32_t writeCount = getDSlayoutBindingsForUTB(nullptr); - const uint32_t infoCount = 1u + // for the index buffer - getFloatBufferBindingsCnt() + - getIntBufferBindingsCnt() + - getUintBufferBindingsCnt(); - if (!outWrites || !outInfo) - return std::make_pair(writeCount, infoCount); - - auto* info = outInfo; - auto fillInfoStruct = [&](E_UTB_ARRAY_TYPE utbArrayType) - { - for (auto virtualAttribData : m_virtualAttribConfig.utbs[utbArrayType]) - { - E_FORMAT format = virtualAttribData.first; - switch (format) - { - case EF_A2B10G10R10_SNORM_PACK32: - format = EF_R32_UINT; - break; - default: - break; - } - info[virtualAttribData.second].desc = createBufferView(core::smart_refctd_ptr(m_packerDataStore.vertexBuffer),format); - info[virtualAttribData.second].buffer.offset = 0u; - info[virtualAttribData.second].buffer.size = m_packerDataStore.vertexBuffer->getSize(); - } - info += m_virtualAttribConfig.utbs[utbArrayType].size(); - }; - - auto* write = outWrites; - auto writeBinding = [&write,dstSet,&info](uint32_t binding, uint32_t count) - { - write->binding = binding; - write->arrayElement = 0u; - write->count = count; - write->descriptorType = asset::IDescriptor::E_TYPE::ET_UNIFORM_TEXEL_BUFFER; - write->dstSet = dstSet; - write->info = info; - write++; - }; - - writeBinding(params.usamplersBinding, 1u + getUintBufferBindingsCnt()); - if (getUintBufferBindingsCnt()) - fillInfoStruct(E_UTB_ARRAY_TYPE::EUAT_UINT); - info->desc = createBufferView(core::smart_refctd_ptr(m_packerDataStore.indexBuffer),EF_R16_UINT); - info->buffer.offset = 0u; - info->buffer.size = m_packerDataStore.indexBuffer->getSize(); - info++; - if (getFloatBufferBindingsCnt()) - { - writeBinding(params.fsamplersBinding, getFloatBufferBindingsCnt()); - fillInfoStruct(E_UTB_ARRAY_TYPE::EUAT_FLOAT); - } - if (getIntBufferBindingsCnt()) - { - writeBinding(params.isamplersBinding, getIntBufferBindingsCnt()); - fillInfoStruct(E_UTB_ARRAY_TYPE::EUAT_INT); - } - - return std::make_pair(writeCount, infoCount); - } - - // the following cannot be called before 'instantiateDataStorage' - struct DSLayoutParamsSSBO - { - uint32_t uintBufferBinding = 0u; - uint32_t uvec2BufferBinding = 1u; - uint32_t uvec3BufferBinding = 2u; - uint32_t uvec4BufferBinding = 3u; - uint32_t indexBufferBinding = 4u; - }; - inline std::string getGLSLForSSBO(uint32_t descriptorSet = 0u, const DSLayoutParamsSSBO& params = {}) - { - std::string result = "#define _NBL_VG_USE_SSBO\n"; - result += "#define _NBL_VG_SSBO_DESCRIPTOR_SET " + std::to_string(descriptorSet) + '\n'; - - if (m_virtualAttribConfig.isUintBufferUsed) - { - result += "#define _NBL_VG_USE_SSBO_UINT\n"; - result += "#define _NBL_VG_SSBO_UINT_BINDING " + std::to_string(params.uintBufferBinding) + '\n'; - } - if (m_virtualAttribConfig.isUvec2BufferUsed) - { - result += "#define _NBL_VG_USE_SSBO_UVEC2\n"; - result += "#define _NBL_VG_SSBO_UVEC2_BINDING " + std::to_string(params.uvec2BufferBinding) + '\n'; - } - if (m_virtualAttribConfig.isUvec3BufferUsed) - { - result += "#define _NBL_VG_USE_SSBO_UVEC3\n"; - result += "#define _NBL_VG_SSBO_UVEC3_BINDING " + std::to_string(params.uvec3BufferBinding) + '\n'; - } - if (m_virtualAttribConfig.isUvec4BufferUsed) - { - result += "#define _NBL_VG_USE_SSBO_UVEC4\n"; - result += "#define _NBL_VG_SSBO_UVEC4_BINDING " + std::to_string(params.uvec4BufferBinding) + '\n'; - } - - result += "#define _NBL_VG_USE_SSBO_INDEX\n"; - result += "#define _NBL_VG_SSBO_INDEX_BINDING " + std::to_string(params.indexBufferBinding) + '\n'; - return result; - } - - inline uint32_t getDSlayoutBindingsForSSBO(typename DescriptorSetLayoutType::SBinding* outBindings, const DSLayoutParamsSSBO& params = {}) const - { - const uint32_t bindingCount = 1 + // for the index buffer - m_virtualAttribConfig.isUintBufferUsed + - m_virtualAttribConfig.isUvec2BufferUsed + - m_virtualAttribConfig.isUvec3BufferUsed + - m_virtualAttribConfig.isUvec4BufferUsed; - - if (outBindings) - { - auto* bnd = outBindings; - auto fillBinding = [&bnd](uint32_t binding) - { - bnd->binding = binding; - bnd->count = 1u; - bnd->stageFlags = asset::ISpecializedShader::ESS_ALL; - bnd->type = asset::IDescriptor::E_TYPE::ET_STORAGE_BUFFER; - bnd->samplers = nullptr; - bnd++; - }; - - if (m_virtualAttribConfig.isUintBufferUsed) - fillBinding(params.uintBufferBinding); - if (m_virtualAttribConfig.isUvec2BufferUsed) - fillBinding(params.uvec2BufferBinding); - if (m_virtualAttribConfig.isUvec3BufferUsed) - fillBinding(params.uvec3BufferBinding); - if (m_virtualAttribConfig.isUvec4BufferUsed) - fillBinding(params.uvec4BufferBinding); - fillBinding(params.indexBufferBinding); - } - return bindingCount; - } - - // info count is always 2 - inline uint32_t getDescriptorSetWritesForSSBO( - typename DescriptorSetType::SWriteDescriptorSet* outWrites, typename DescriptorSetType::SDescriptorInfo* outInfo, DescriptorSetType* dstSet, - const DSLayoutParamsSSBO& params = {} - ) const - { - const uint32_t writeCount = getDSlayoutBindingsForSSBO(nullptr); - if (!outWrites || !outInfo) - return writeCount; - - auto* write = outWrites; - auto info = outInfo; - auto fillWriteStruct = [&](uint32_t binding) - { - write->binding = binding; - write->arrayElement = 0u; - write->count = 1u; - write->descriptorType = IDescriptor::E_TYPE::ET_STORAGE_BUFFER; - write->dstSet = dstSet; - write->info = info; - write++; - }; - if (m_virtualAttribConfig.isUintBufferUsed) - fillWriteStruct(params.uintBufferBinding); - if (m_virtualAttribConfig.isUvec2BufferUsed) - fillWriteStruct(params.uvec2BufferBinding); - if (m_virtualAttribConfig.isUvec3BufferUsed) - fillWriteStruct(params.uvec3BufferBinding); - if (m_virtualAttribConfig.isUvec4BufferUsed) - fillWriteStruct(params.uvec4BufferBinding); - info->desc = m_packerDataStore.vertexBuffer; - info->buffer.offset = 0u; - info->buffer.size = m_packerDataStore.vertexBuffer->getSize(); - info++; - - fillWriteStruct(params.indexBufferBinding); - info->desc = m_packerDataStore.indexBuffer; - info->buffer.offset = 0u; - info->buffer.size = m_packerDataStore.indexBuffer->getSize(); - info++; - - return writeCount; - } - - template - bool alloc(ReservedAllocationMeshBuffers* rambOut, const MeshBufferIterator mbBegin, const MeshBufferIterator mbEnd); - - void free(const ReservedAllocationMeshBuffers* rambIn, uint32_t meshBuffersToFreeCnt) - { - for (uint32_t i = 0u; i < meshBuffersToFreeCnt; i++) - { - const ReservedAllocationMeshBuffers& ramb = rambIn[i]; - - const ReservedAllocationMeshBuffers* const ramb = rambIn + i; - - if (ramb->indexAllocationOffset != base_t::INVALID_ADDRESS) - base_t::m_idxBuffAlctr.free_addr(ramb->indexAllocationOffset, ramb->indexAllocationReservedCnt); - - if (ramb->mdiAllocationOffset != base_t::INVALID_ADDRESS) - base_t::m_MDIDataAlctr.free_addr(ramb->mdiAllocationOffset, ramb->mdiAllocationReservedCnt); - - for (uint32_t j = 0; j < SVertexInputParams::MAX_VERTEX_ATTRIB_COUNT; j++) - { - const AttribAllocParams& attrAllocParams = ramb->attribAllocParams[j]; - if (attrAllocParams.offset != base_t::INVALID_ADDRESS) - base_t::m_vtxBuffAlctr.free_addr(attrAllocParams.offset, attrAllocParams.size); - } - } - } - - inline const PackerDataStore& getPackerDataStore() const { return m_packerDataStore; } - - const core::GeneralpurposeAddressAllocator& getMDIAllocator() const { return base_t::m_MDIDataAlctr; } - const core::GeneralpurposeAddressAllocator& getIndexAllocator() const { return base_t::m_idxBuffAlctr; } - const core::GeneralpurposeAddressAllocator& getVertexAllocator() const { return base_t::m_vtxBuffAlctr; } - -protected: - IMeshPackerV2(const AllocationParams& allocParams, const SupportedFormatsContainer& formats, uint16_t minTriangleCountPerMDIData, uint16_t maxTriangleCountPerMDIData) - : base_t(minTriangleCountPerMDIData, maxTriangleCountPerMDIData), - IMeshPackerV2Base(formats) - { - base_t::initializeCommonAllocators(allocParams); - }; - template - explicit IMeshPackerV2(const IMeshPackerV2* otherMp) - : base_t(otherMp->getMinTriangleCountPerMDI(),otherMp->getMaxTriangleCountPerMDI()), - IMeshPackerV2Base(otherMp->getVirtualAttribConfig()) - { - base_t::initializeCommonAllocators( - otherMp->getMDIAllocator(), - otherMp->getIndexAllocator(), - otherMp->getVertexAllocator() - ); - }; - - template - void freeAllocatedAddressesOnAllocFail(ReservedAllocationMeshBuffers* rambOut, const MeshBufferIterator mbBegin, const MeshBufferIterator mbEnd) - { - size_t i = 0ull; - for (auto it = mbBegin; it != mbEnd; it++) - { - ReservedAllocationMeshBuffers& ramb = *(rambOut + i); - - if (ramb.indexAllocationOffset == base_t::INVALID_ADDRESS) - return; - - base_t::m_idxBuffAlctr.free_addr(ramb.indexAllocationOffset, ramb.indexAllocationReservedCnt); - - const auto& mbVtxInputParams = (*it)->getPipeline()->getVertexInputParams(); - for (uint16_t attrBit = 0x0001, location = 0; location < SVertexInputParams::MAX_ATTR_BUF_BINDING_COUNT; attrBit <<= 1, location++) - { - if (!(attrBit & mbVtxInputParams.enabledAttribFlags)) - continue; - - if (ramb.attribAllocParams[location].offset == base_t::INVALID_ADDRESS) - return; - - base_t::m_vtxBuffAlctr.free_addr(ramb.attribAllocParams[location].offset, ramb.attribAllocParams[location].size); - } - - if (ramb.mdiAllocationOffset == base_t::INVALID_ADDRESS) - return; - - base_t::m_MDIDataAlctr.free_addr(ramb.mdiAllocationOffset, ramb.mdiAllocationReservedCnt); - - i++; - } - } - - PackerDataStore m_packerDataStore; - -}; - -template -template -bool IMeshPackerV2::alloc(ReservedAllocationMeshBuffers* rambOut, const MeshBufferIterator mbBegin, const MeshBufferIterator mbEnd) -{ - size_t i = 0ull; - for (auto it = mbBegin; it != mbEnd; it++) - { - ReservedAllocationMeshBuffers& ramb = *(rambOut + i); - const size_t idxCnt = base_t::calcIdxCntAfterConversionToTriangleList(*it); - const size_t maxVtxCnt = base_t::calcVertexCountBoundWithBatchDuplication(*it); - const uint32_t insCnt = (*it)->getInstanceCount(); - - //allocate indices - ramb.indexAllocationOffset = base_t::m_idxBuffAlctr.alloc_addr(idxCnt, 1u); - if (ramb.indexAllocationOffset == base_t::INVALID_ADDRESS) - { - freeAllocatedAddressesOnAllocFail(rambOut, mbBegin, mbEnd); - return false; - } - ramb.indexAllocationReservedCnt = idxCnt; - - //allocate vertices - const auto& mbVtxInputParams = (*it)->getPipeline()->getVertexInputParams(); - for (uint16_t attrBit = 0x0001, location = 0; location < SVertexInputParams::MAX_ATTR_BUF_BINDING_COUNT; attrBit <<= 1, location++) - { - if (!(attrBit & mbVtxInputParams.enabledAttribFlags)) - continue; - - const E_FORMAT attribFormat = static_cast(mbVtxInputParams.attributes[location].format); - - if (!m_virtualAttribConfig.isFormatSupported(attribFormat)) - return false; - - const uint32_t attribSize = asset::getTexelOrBlockBytesize(attribFormat); - const uint32_t binding = mbVtxInputParams.attributes[location].binding; - const E_VERTEX_INPUT_RATE inputRate = mbVtxInputParams.bindings[binding].inputRate; - - if (inputRate == EVIR_PER_VERTEX) - { - const uint32_t allocByteSize = maxVtxCnt * attribSize; - ramb.attribAllocParams[location].offset = base_t::m_vtxBuffAlctr.alloc_addr(allocByteSize, attribSize); - ramb.attribAllocParams[location].size = allocByteSize; - - if(ramb.attribAllocParams[location].offset == base_t::INVALID_ADDRESS) - { - freeAllocatedAddressesOnAllocFail(rambOut, mbBegin, mbEnd); - return false; - } - } - else if (inputRate == EVIR_PER_INSTANCE) - { - const uint32_t allocByteSize = insCnt * attribSize; - ramb.attribAllocParams[location].offset = base_t::m_vtxBuffAlctr.alloc_addr(allocByteSize, attribSize); - ramb.attribAllocParams[location].size = allocByteSize; - - if (ramb.attribAllocParams[location].offset == base_t::INVALID_ADDRESS) - { - freeAllocatedAddressesOnAllocFail(rambOut, mbBegin, mbEnd); - return false; - } - } - - if (ramb.attribAllocParams[location].offset == base_t::INVALID_ADDRESS) - return false; - } - - //allocate MDI structs - const uint32_t minIdxCntPerPatch = base_t::m_minTriangleCountPerMDIData * 3; - size_t possibleMDIStructsNeededCnt = (idxCnt + minIdxCntPerPatch - 1) / minIdxCntPerPatch; - - ramb.mdiAllocationOffset = base_t::m_MDIDataAlctr.alloc_addr(possibleMDIStructsNeededCnt, 1u); - if (ramb.mdiAllocationOffset == base_t::INVALID_ADDRESS) - { - freeAllocatedAddressesOnAllocFail(rambOut, mbBegin, mbEnd); - return false; - } - ramb.mdiAllocationReservedCnt = possibleMDIStructsNeededCnt; - - i++; - } - - return true; -} -#endif -} -} - -#endif \ No newline at end of file diff --git a/include/nbl/asset/utils/phmap_deserialization.h b/include/nbl/asset/utils/phmap_deserialization.h index fa4dd1637e..790b729705 100644 --- a/include/nbl/asset/utils/phmap_deserialization.h +++ b/include/nbl/asset/utils/phmap_deserialization.h @@ -1,9 +1,8 @@ // Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_PHMAP_DESERIALIZATION_H_INCLUDED__ -#define __NBL_ASSET_PHMAP_DESERIALIZATION_H_INCLUDED__ +#ifndef _NBL_ASSET_PHMAP_DESERIALIZATION_H_INCLUDED_ +#define _NBL_ASSET_PHMAP_DESERIALIZATION_H_INCLUDED_ #include "nbl/core/declarations.h" #include "nbl/asset/ICPUBuffer.h" @@ -20,7 +19,7 @@ class CBufferPhmapInputArchive } // TODO: protect against reading out of the buffer range - bool load(char* p, size_t sz) + bool loadBinary(void* p, size_t sz) { memcpy(p, buffPtr, sz); buffPtr += sz; @@ -29,7 +28,7 @@ class CBufferPhmapInputArchive } template - typename std::enable_if::value,bool>::type load(V* v) + typename std::enable_if::value,bool>::type loadBinary(V* v) { memcpy(reinterpret_cast(v), buffPtr, sizeof(V)); buffPtr += sizeof(V); diff --git a/include/nbl/asset/utils/phmap_serialization.h b/include/nbl/asset/utils/phmap_serialization.h index 9a6e368c6b..fe7ce64196 100644 --- a/include/nbl/asset/utils/phmap_serialization.h +++ b/include/nbl/asset/utils/phmap_serialization.h @@ -1,9 +1,8 @@ // Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_PHMAP_SERIALIZATION_H_INCLUDED__ -#define __NBL_ASSET_PHMAP_SERIALIZATION_H_INCLUDED__ +#ifndef _NBL_ASSET_PHMAP_SERIALIZATION_H_INCLUDED_ +#define _NBL_ASSET_PHMAP_SERIALIZATION_H_INCLUDED_ #include "nbl/core/declarations.h" #include "nbl/asset/ICPUBuffer.h" @@ -20,7 +19,7 @@ class CBufferPhmapOutputArchive } // TODO: protect against writing out of bounds as defined by SBufferRange - bool dump(const char* p, size_t sz) + bool saveBinary(const void* p, size_t sz) { memcpy(bufferPtr, p, sz); bufferPtr += sz; @@ -29,7 +28,7 @@ class CBufferPhmapOutputArchive } template - typename std::enable_if::value,bool>::type dump(const V& v) + typename std::enable_if::value,bool>::type saveBinary(const V& v) { memcpy(bufferPtr, reinterpret_cast(&v), sizeof(V)); bufferPtr += sizeof(V); diff --git a/include/nbl/builtin/hlsl/bxdf/bxdf_traits.hlsl b/include/nbl/builtin/hlsl/bxdf/bxdf_traits.hlsl new file mode 100644 index 0000000000..5ef93e7abf --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/bxdf_traits.hlsl @@ -0,0 +1,107 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_BXDF_TRAITS_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_TRAITS_INCLUDED_ + +#include "nbl/builtin/hlsl/bxdf/common.hlsl" +#include "nbl/builtin/hlsl/bxdf/reflection.hlsl" +#include "nbl/builtin/hlsl/bxdf/transmission.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ + +enum BxDFType : uint16_t +{ + BT_BSDF = 0, + BT_BRDF, + BT_BTDF +}; + +template +struct traits; + +template +struct traits > +{ + NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BRDF; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = false; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; +}; + +template +struct traits > +{ + NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BRDF; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; +}; + +// no blinn phong + +template +struct traits > +{ + NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BRDF; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; +}; + +template +struct traits > +{ + NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BRDF; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; +}; + + +template +struct traits > +{ + NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = false; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; +}; + +template +struct traits > +{ + NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; +}; + +template +struct traits > +{ + NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; +}; + +template +struct traits > +{ + NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; +}; + +template +struct traits > +{ + NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; +}; + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index c6e8679d3b..36e0f883ec 100644 --- a/include/nbl/builtin/hlsl/bxdf/common.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/common.hlsl @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2023 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h #ifndef _NBL_BUILTIN_HLSL_BXDF_COMMON_INCLUDED_ @@ -6,39 +6,165 @@ #include "nbl/builtin/hlsl/limits.hlsl" #include "nbl/builtin/hlsl/numbers.hlsl" -#include "nbl/builtin/hlsl/math/functions.glsl" +#include "nbl/builtin/hlsl/type_traits.hlsl" +#include "nbl/builtin/hlsl/concepts.hlsl" +#include "nbl/builtin/hlsl/ieee754.hlsl" +#include "nbl/builtin/hlsl/tgmath.hlsl" +#include "nbl/builtin/hlsl/math/functions.hlsl" +#include "nbl/builtin/hlsl/cpp_compat/promote.hlsl" +#include "nbl/builtin/hlsl/bxdf/fresnel.hlsl" +#include "nbl/builtin/hlsl/sampling/quotient_and_pdf.hlsl" +#include "nbl/builtin/hlsl/vector_utils/vector_traits.hlsl" namespace nbl { namespace hlsl { + namespace bxdf { +template) +struct ComputeMicrofacetNormal +{ + using vector_type = T; + using scalar_type = typename vector_traits::scalar_type; + + static ComputeMicrofacetNormal create(NBL_CONST_REF_ARG(vector_type) V, NBL_CONST_REF_ARG(vector_type) L, NBL_CONST_REF_ARG(vector_type) H, scalar_type eta) + { + ComputeMicrofacetNormal retval; + retval.V = V; + retval.L = L; + retval.orientedEta = fresnel::OrientedEtas::create(hlsl::dot(V, H), eta); + return retval; + } + + // NDFs are defined in terms of `abs(NdotH)` and microfacets are two sided. Note that `N` itself is the definition of the upper hemisphere direction. + // The possible directions of L form a cone around -V with the cosine of the angle equal higher or equal to min(orientedEta, 1.f/orientedEta), and vice versa. + // This means that for: + // - Eta>1 the L will be longer than V projected on V, and VdotH<0 for all L + // - whereas with Eta<1 the L is shorter, and VdotH>0 for all L + // Because to be a refraction `VdotH` and `LdotH` must differ in sign, so whenever one is positive the other is negative. + // Since we're considering single scattering, the V and L must enter the microfacet described by H same way they enter the macro-medium described by N. + // All this means that by looking at the sign of VdotH we can also tell the sign of VdotN. + // However the whole `V+L*eta` formula is backwards because what it should be is `-V-L*eta` so the sign flip is applied just to restore the H-finding to that value. + + // The math: + // dot(V,H) = V2 + VdotL*eta = 1 + VdotL*eta, note that VdotL<=1 so VdotH>=0 when eta==1 + // then with valid transmission path constraint: + // VdotH <= 1-orientedEta2 for orientedEta<1 -> VdotH<0 + // VdotH <= 0 for orientedEta>1 + // so for transmission VdotH<=0, H needs to be flipped to be consistent with oriented eta + vector_type unnormalized(const bool _refract) + { + const scalar_type etaFactor = hlsl::mix(scalar_type(1.0), orientedEta.value, _refract); + vector_type tmpH = V + L * etaFactor; + tmpH = ieee754::flipSign(tmpH, _refract); + return tmpH; + } + + // returns normalized vector, but NaN when result is length 0 + vector_type normalized(const bool _refract) + { + const vector_type H = unnormalized(_refract); + return hlsl::normalize(H); + } + + // if V and L are on different sides of the surface normal, then their dot product sign bits will differ, hence XOR will yield 1 at last bit + static bool isTransmissionPath(float NdotV, float NdotL) + { + return bool((bit_cast(NdotV) ^ bit_cast(NdotL)) & 0x80000000u); + } + + vector_type V; + vector_type L; + fresnel::OrientedEtas orientedEta; +}; + namespace ray_dir_info { -// no ray-differentials, nothing -struct Basic +#define NBL_CONCEPT_NAME Basic +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (rdirinfo, T) +#define NBL_CONCEPT_PARAM_1 (N, typename T::vector3_type) +#define NBL_CONCEPT_PARAM_2 (dirDotN, typename T::scalar_type) +#define NBL_CONCEPT_PARAM_3 (m, typename T::matrix3x3_type) +#define NBL_CONCEPT_PARAM_4 (rfl, Reflect) +#define NBL_CONCEPT_PARAM_5 (rfr, Refract) +NBL_CONCEPT_BEGIN(6) +#define rdirinfo NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define N NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define dirDotN NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +#define m NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +#define rfl NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 +#define rfr NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::matrix3x3_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((rdirinfo.getDirection()), ::nbl::hlsl::is_same_v, typename T::vector3_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((rdirinfo.transmit()), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((rdirinfo.reflect(rfl)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((rdirinfo.refract(rfr)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((rdirinfo.transform(m)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(is_scalar_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(is_vector_v, typename T::vector3_type)) +); +#undef rfr +#undef rfl +#undef m +#undef dirDotN +#undef N +#undef rdirinfo +#include + +template +struct SBasic { - float3 getDirection() {return direction;} - - Basic transmit() - { - Basic retval; - retval.direction = -direction; - return retval; - } - - Basic reflect(const float3 N, const float directionDotN) - { - Basic retval; - retval.direction = nbl::hlsl::reflect(direction,N,directionDotN); - return retval; - } - - float3 direction; + using scalar_type = T; + using vector3_type = vector; + using matrix3x3_type = matrix; + + vector3_type getDirection() NBL_CONST_MEMBER_FUNC { return direction; } + + SBasic transmit() + { + SBasic retval; + retval.direction = -direction; + return retval; + } + + SBasic reflect(NBL_CONST_REF_ARG(Reflect) r) + { + SBasic retval; + retval.direction = r(); + return retval; + } + + SBasic refract(NBL_CONST_REF_ARG(Refract) r) + { + SBasic retval; + retval.direction = r(); + return retval; + } + + // WARNING: matrix must be orthonormal + SBasic transform(NBL_CONST_REF_ARG(matrix3x3_type) m) NBL_CONST_MEMBER_FUNC + { + matrix3x3_type m_T = nbl::hlsl::transpose(m); + assert(nbl::hlsl::abs(nbl::hlsl::dot(m_T[0], m_T[1])) < 1e-5); + assert(nbl::hlsl::abs(nbl::hlsl::dot(m_T[0], m_T[2])) < 1e-5); + assert(nbl::hlsl::abs(nbl::hlsl::dot(m_T[1], m_T[2])) < 1e-5); + + SBasic retval; + retval.direction = nbl::hlsl::mul(m, direction); + return retval; + } + + vector3_type direction; }; // more to come! @@ -48,358 +174,738 @@ struct Basic namespace surface_interactions { -template -struct Isotropic +#define NBL_CONCEPT_NAME Isotropic +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (iso, T) +#define NBL_CONCEPT_PARAM_1 (normV, typename T::ray_dir_info_type) +#define NBL_CONCEPT_PARAM_2 (normN, typename T::vector3_type) +NBL_CONCEPT_BEGIN(3) +#define iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define normV NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define normN NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::ray_dir_info_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getV()), ::nbl::hlsl::is_same_v, typename T::ray_dir_info_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getN()), ::nbl::hlsl::is_same_v, typename T::vector3_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getNdotV()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getNdotV2()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(normV,normN)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(ray_dir_info::Basic, typename T::ray_dir_info_type)) +); +#undef normN +#undef normV +#undef iso +#include + +template) +struct SIsotropic { - // WARNING: Changed since GLSL, now arguments need to be normalized! - static Isotropic create(const RayDirInfo normalizedV, const float3 normalizedN) - { - Isotropic retval; - retval.V = normalizedV; - retval.N = normalizedN; - - retval.NdotV = dot(retval.N,retval.V.getDirection()); - retval.NdotV_squared = retval.NdotV*retval.NdotV; - - return retval; - } - - RayDirInfo V; - float3 N; - float NdotV; - float NdotV2; // old NdotV_squared + using ray_dir_info_type = RayDirInfo; + using scalar_type = typename RayDirInfo::scalar_type; + using vector3_type = typename RayDirInfo::vector3_type; + + // WARNING: Changed since GLSL, now arguments need to be normalized! + static SIsotropic create(NBL_CONST_REF_ARG(RayDirInfo) normalizedV, NBL_CONST_REF_ARG(vector3_type) normalizedN) + { + SIsotropic retval; + retval.V = normalizedV; + retval.N = normalizedN; + retval.NdotV = nbl::hlsl::dot(retval.N, retval.V.getDirection()); + retval.NdotV2 = retval.NdotV * retval.NdotV; + + return retval; + } + + RayDirInfo getV() NBL_CONST_MEMBER_FUNC { return V; } + vector3_type getN() NBL_CONST_MEMBER_FUNC { return N; } + scalar_type getNdotV() NBL_CONST_MEMBER_FUNC { return NdotV; } + scalar_type getNdotV2() NBL_CONST_MEMBER_FUNC { return NdotV2; } + + RayDirInfo V; + vector3_type N; + scalar_type NdotV; + scalar_type NdotV2; }; -template -struct Anisotropic : Isotropic +#define NBL_CONCEPT_NAME Anisotropic +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (aniso, T) +#define NBL_CONCEPT_PARAM_1 (iso, typename T::isotropic_interaction_type) +#define NBL_CONCEPT_PARAM_2 (normT, typename T::vector3_type) +NBL_CONCEPT_BEGIN(3) +#define aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define normT NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::ray_dir_info_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::isotropic_interaction_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::matrix3x3_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((aniso.getT()), ::nbl::hlsl::is_same_v, typename T::vector3_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((aniso.getB()), ::nbl::hlsl::is_same_v, typename T::vector3_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((aniso.getTdotV()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((aniso.getBdotV()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(iso,normT,normT)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((aniso.getTangentSpaceV()), ::nbl::hlsl::is_same_v, typename T::vector3_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((aniso.getToTangentSpace()), ::nbl::hlsl::is_same_v, typename T::matrix3x3_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((aniso.getFromTangentSpace()), ::nbl::hlsl::is_same_v, typename T::matrix3x3_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(Isotropic, typename T::isotropic_interaction_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(ray_dir_info::Basic, typename T::ray_dir_info_type)) +); +#undef normT +#undef iso +#undef aniso +#include + +template) +struct SAnisotropic { - // WARNING: Changed since GLSL, now arguments need to be normalized! - static Anisotropic create( - const Isotropic isotropic, - const float3 normalizedT, - const float normalizedB - ) - { - Anisotropic retval; - retval::Isotropic = isotropic; - retval.T = normalizedT; - retval.B = normalizedB; - - const float3 V = retval.getDirection(); - retval.TdotV = dot(V,retval.T); - retval.BdotV = dot(V,retval.B); - - return retval; - } - static Anisotropic create(const Isotropic isotropic, const float3 normalizedT) - { - return create(isotropic,normalizedT,cross(isotropic.N,normalizedT)); - } - static Anisotropic create(const Isotropic isotropic) - { - float2x3 TB = nbl::hlsl::frisvad(isotropic.N); - return create(isotropic,TB[0],TB[1]); - } - - float3 getTangentSpaceV() {return float3(Tdot,BdotV,Isotropic::NdotV);} - // WARNING: its the transpose of the old GLSL function return value! - float3x3 getTangentFrame() {return float3x3(T,B,Isotropic::N);} - - float3 T; - float3 B; - float3 TdotV; - float3 BdotV; + using this_t = SAnisotropic; + using isotropic_interaction_type = IsotropicInteraction; + using ray_dir_info_type = typename isotropic_interaction_type::ray_dir_info_type; + using scalar_type = typename ray_dir_info_type::scalar_type; + using vector3_type = typename ray_dir_info_type::vector3_type; + using matrix3x3_type = matrix; + + // WARNING: Changed since GLSL, now arguments need to be normalized! + static this_t create( + NBL_CONST_REF_ARG(isotropic_interaction_type) isotropic, + NBL_CONST_REF_ARG(vector3_type) normalizedT, + NBL_CONST_REF_ARG(vector3_type) normalizedB + ) + { + this_t retval; + retval.isotropic = isotropic; + + retval.T = normalizedT; + retval.B = normalizedB; + + retval.TdotV = nbl::hlsl::dot(retval.isotropic.getV().getDirection(), retval.T); + retval.BdotV = nbl::hlsl::dot(retval.isotropic.getV().getDirection(), retval.B); + + return retval; + } + static this_t create(NBL_CONST_REF_ARG(isotropic_interaction_type) isotropic, NBL_CONST_REF_ARG(vector3_type) normalizedT) + { + return create(isotropic, normalizedT, cross(isotropic.getN(), normalizedT)); + } + static this_t create(NBL_CONST_REF_ARG(isotropic_interaction_type) isotropic) + { + vector3_type T, B; + math::frisvad(isotropic.getN(), T, B); + return create(isotropic, nbl::hlsl::normalize(T), nbl::hlsl::normalize(B)); + } + + static this_t create(NBL_CONST_REF_ARG(ray_dir_info_type) normalizedV, NBL_CONST_REF_ARG(vector3_type) normalizedN) + { + isotropic_interaction_type isotropic = isotropic_interaction_type::create(normalizedV, normalizedN); + return create(isotropic); + } + + ray_dir_info_type getV() NBL_CONST_MEMBER_FUNC { return isotropic.V; } + vector3_type getN() NBL_CONST_MEMBER_FUNC { return isotropic.N; } + scalar_type getNdotV() NBL_CONST_MEMBER_FUNC { return isotropic.NdotV; } + scalar_type getNdotV2() NBL_CONST_MEMBER_FUNC { return isotropic.NdotV2; } + + vector3_type getT() NBL_CONST_MEMBER_FUNC { return T; } + vector3_type getB() NBL_CONST_MEMBER_FUNC { return B; } + scalar_type getTdotV() NBL_CONST_MEMBER_FUNC { return TdotV; } + scalar_type getBdotV() NBL_CONST_MEMBER_FUNC { return BdotV; } + + vector3_type getTangentSpaceV() NBL_CONST_MEMBER_FUNC { return vector3_type(TdotV, BdotV, isotropic.NdotV); } + matrix3x3_type getToTangentSpace() NBL_CONST_MEMBER_FUNC { return matrix3x3_type(T, B, isotropic.N); } + matrix3x3_type getFromTangentSpace() NBL_CONST_MEMBER_FUNC { return nbl::hlsl::transpose(matrix3x3_type(T, B, isotropic.N)); } + + isotropic_interaction_type isotropic; + vector3_type T; + vector3_type B; + scalar_type TdotV; + scalar_type BdotV; }; } -template -struct LightSample +#define NBL_CONCEPT_NAME LightSample +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (_sample, T) +#define NBL_CONCEPT_PARAM_1 (inter, surface_interactions::SIsotropic) +#define NBL_CONCEPT_PARAM_2 (rdirinfo, typename T::ray_dir_info_type) +#define NBL_CONCEPT_PARAM_3 (pV, typename T::vector3_type) +#define NBL_CONCEPT_PARAM_4 (frame, typename T::matrix3x3_type) +#define NBL_CONCEPT_PARAM_5 (pVdotL, typename T::scalar_type) +NBL_CONCEPT_BEGIN(6) +#define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define inter NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define rdirinfo NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +#define pV NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +#define frame NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 +#define pVdotL NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::ray_dir_info_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::matrix3x3_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((_sample.getL()), ::nbl::hlsl::is_same_v, typename T::ray_dir_info_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((_sample.getVdotL()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((_sample.getTdotL()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((_sample.getBdotL()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((_sample.getNdotL()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((_sample.getNdotL2()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::createFromTangentSpace(pV,rdirinfo,frame)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(rdirinfo,pVdotL,pV)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(rdirinfo,pVdotL,pV,pV,pV)), ::nbl::hlsl::is_same_v, T)) + // ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template create >(pV,inter)), ::nbl::hlsl::is_same_v, T)) // NOTE: temporarily commented out due to dxc bug https://github.com/microsoft/DirectXShaderCompiler/issues/7154 + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((_sample.getTangentSpaceL()), ::nbl::hlsl::is_same_v, typename T::vector3_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(ray_dir_info::Basic, typename T::ray_dir_info_type)) +); +#undef pVdotL +#undef frame +#undef pV +#undef rdirinfo +#undef inter +#undef _sample +#include + +template) +struct SLightSample { - static LightSample createTangentSpace( - const float3 tangentSpaceV, - const RayDirInfo tangentSpaceL, - const float3x3 tangentFrame // WARNING: its the transpose of the old GLSL function return value! - ) - { - LightSample retval; - - retval.L = RayDirInfo::transform(tangentSpaceL,tangentFrame); - retval.VdotL = dot(tangentSpaceV,tangentSpaceL); + using this_t = SLightSample; + using ray_dir_info_type = RayDirInfo; + using scalar_type = typename ray_dir_info_type::scalar_type; + using vector3_type = typename ray_dir_info_type::vector3_type; + using matrix3x3_type = matrix; + + static this_t createFromTangentSpace( + NBL_CONST_REF_ARG(vector3_type) tangentSpaceV, + NBL_CONST_REF_ARG(ray_dir_info_type) tangentSpaceL, + NBL_CONST_REF_ARG(matrix3x3_type) tangentFrame + ) + { + this_t retval; - retval.TdotL = tangentSpaceL.x; - retval.BdotL = tangentSpaceL.y; - retval.NdotL = tangentSpaceL.z; - retval.NdotL2 = retval.NdotL*retval.NdotL; - - return retval; - } - static LightSample create(const RayDirInfo L, const float VdotL, const float3 N) - { - LightSample retval; - - retval.L = L; - retval.VdotL = VdotL; + const vector3_type tsL = tangentSpaceL.getDirection(); + retval.L = tangentSpaceL.transform(tangentFrame); + retval.VdotL = nbl::hlsl::dot(tangentSpaceV, tsL); - retval.TdotL = nbl::hlsl::numeric_limits::nan(); - retval.BdotL = nbl::hlsl::numeric_limits::nan(); - retval.NdotL = dot(N,L); - retval.NdotL2 = retval.NdotL*retval.NdotL; - - return retval; - } - static LightSample create(const RayDirInfo L, const float VdotL, const float3 T, const float3 B, const float3 N) - { - LightSample retval = create(L,VdotL,N); - - retval.TdotL = dot(T,L); - retval.BdotL = dot(B,L); + retval.TdotL = tsL.x; + retval.BdotL = tsL.y; + retval.NdotL = tsL.z; + retval.NdotL2 = retval.NdotL*retval.NdotL; + + return retval; + } + static this_t create(NBL_CONST_REF_ARG(ray_dir_info_type) L, const scalar_type VdotL, NBL_CONST_REF_ARG(vector3_type) N) + { + this_t retval; + + retval.L = L; + retval.VdotL = VdotL; + + retval.TdotL = nbl::hlsl::numeric_limits::quiet_NaN; + retval.BdotL = nbl::hlsl::numeric_limits::quiet_NaN; + retval.NdotL = nbl::hlsl::dot(N,L.getDirection()); + retval.NdotL2 = retval.NdotL * retval.NdotL; + + return retval; + } + static this_t create(NBL_CONST_REF_ARG(ray_dir_info_type) L, const scalar_type VdotL, NBL_CONST_REF_ARG(vector3_type) T, NBL_CONST_REF_ARG(vector3_type) B, NBL_CONST_REF_ARG(vector3_type) N) + { + this_t retval = create(L,VdotL,N); + + retval.TdotL = nbl::hlsl::dot(T,L.getDirection()); + retval.BdotL = nbl::hlsl::dot(B,L.getDirection()); + + return retval; + } + + template) + static this_t create(NBL_CONST_REF_ARG(vector3_type) L, NBL_CONST_REF_ARG(SurfaceInteraction) interaction) + { + const vector3_type V = interaction.V.getDirection(); + const scalar_type VdotL = nbl::hlsl::dot(V,L); + this_t retval; + if (surface_interactions::Anisotropic) // TODO use NBL_IF_CONSTEXPR when pr #860 is merged + retval = create(L,VdotL,interaction.T,interaction.B,interaction.N); + else + retval = create(L, VdotL, interaction.N); + return retval; + } - return retval; - } - // overloads for surface_interactions - template - static LightSample create(const float3 L, const surface_interactions::Isotropic interaction) - { - const float3 V = interaction.V.getDirection(); - const float VdotL = dot(V,L); - return create(L,VdotL,interaction.N); - } - template - static LightSample create(const float3 L, const surface_interactions::Anisotropic interaction) - { - const float3 V = interaction.V.getDirection(); - const float VdotL = dot(V,L); - return create(L,VdotL,interaction.T,interaction.B,interaction.N); - } - // - float3 getTangentSpaceL() - { - return float3(TdotL,BdotL,NdotL); - } - - RayDirInfo L; - float VdotL; - - float TdotL; - float BdotL; - float NdotL; - float NdotL2; + vector3_type getTangentSpaceL() NBL_CONST_MEMBER_FUNC + { + return vector3_type(TdotL, BdotL, NdotL); + } + + ray_dir_info_type getL() NBL_CONST_MEMBER_FUNC { return L; } + scalar_type getVdotL() NBL_CONST_MEMBER_FUNC { return VdotL; } + scalar_type getTdotL() NBL_CONST_MEMBER_FUNC { return TdotL; } + scalar_type getBdotL() NBL_CONST_MEMBER_FUNC { return BdotL; } + scalar_type getNdotL() NBL_CONST_MEMBER_FUNC { return NdotL; } + scalar_type getNdotL2() NBL_CONST_MEMBER_FUNC { return NdotL2; } + + + RayDirInfo L; + scalar_type VdotL; + + scalar_type TdotL; + scalar_type BdotL; + scalar_type NdotL; + scalar_type NdotL2; }; -// -struct IsotropicMicrofacetCache +#define NBL_CONCEPT_NAME ReadableIsotropicMicrofacetCache +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (cache, T) +#define NBL_CONCEPT_PARAM_1 (pNdotV, typename T::scalar_type) +#define NBL_CONCEPT_PARAM_2 (b0, bool) +NBL_CONCEPT_BEGIN(3) +#define cache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define pNdotV NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define b0 NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((cache.getVdotH()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((cache.getLdotH()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((cache.getNdotH()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((cache.getNdotH2()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((cache.isValidVNDFMicrofacet(b0,b0,pNdotV,pNdotV,pNdotV)), ::nbl::hlsl::is_same_v, bool)) +); +#undef b0 +#undef pNdotV +#undef cache +#include + +#define NBL_CONCEPT_NAME CreatableIsotropicMicrofacetCache +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (cache, T) +#define NBL_CONCEPT_PARAM_1 (iso, surface_interactions::SIsotropic >) +#define NBL_CONCEPT_PARAM_2 (pNdotV, typename T::scalar_type) +#define NBL_CONCEPT_PARAM_3 (_sample, SLightSample >) +#define NBL_CONCEPT_PARAM_4 (V, typename T::vector3_type) +#define NBL_CONCEPT_PARAM_5 (eta, fresnel::OrientedEtas) +NBL_CONCEPT_BEGIN(6) +#define cache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define pNdotV NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +#define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +#define V NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 +#define eta NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::createForReflection(pNdotV,pNdotV,pNdotV,pNdotV)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::createForReflection(pNdotV,pNdotV,pNdotV)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template createForReflection >,SLightSample > >(iso,_sample)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(V,V,V,eta,V)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template create >,SLightSample > >(iso,_sample,eta,V)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(ReadableIsotropicMicrofacetCache, T)) +); +#undef eta +#undef V +#undef _sample +#undef pNdotV +#undef iso +#undef cache +#include + +template ) +struct SIsotropicMicrofacetCache { - // always valid because its specialized for the reflective case - static IsotropicMicrofacetCache createForReflection(const float NdotV, const float NdotL, const float VdotL, out float LplusV_rcpLen) - { - LplusV_rcpLen = inversesqrt(2.0+2.0*VdotL); + using this_t = SIsotropicMicrofacetCache; + using scalar_type = T; + using vector3_type = vector; + using matrix3x3_type = matrix; - IsotropicMicrofacetCache retval; - - retval.VdotH = LplusV_rcpLen*VdotL+LplusV_rcpLen; - retval.LdotH = retval.VdotH; - retval.NdotH = (NdotL+NdotV)*LplusV_rcpLen; - retval.NdotH2 = retval.NdotH*retval.NdotH; - - return retval; - } - static IsotropicMicrofacetCache createForReflection(const float NdotV, const float NdotL, const float VdotL) - { - float dummy; - return createForReflection(NdotV,NdotL,VdotL,dummy); - } - template - static IsotropicMicrofacetCache createForReflection( - const surface_interactions::Isotropic interaction, - const LightSample _sample) - { - return createForReflection(interaction.NdotV,_sample.NdotL,_sample.VdotL); - } - // transmissive cases need to be checked if the path is valid before usage - static bool compute( - out IsotropicMicrofacetCache retval, - const bool transmitted, const float3 V, const float3 L, - const float3 N, const float NdotL, const float VdotL, - const float orientedEta, const float rcpOrientedEta, out float3 H - ) - { - // TODO: can we optimize? - H = computeMicrofacetNormal(transmitted,V,L,orientedEta); - retval.NdotH = dot(N,H); - - // not coming from the medium (reflected) OR - // exiting at the macro scale AND ( (not L outside the cone of possible directions given IoR with constraint VdotH*LdotH<0.0) OR (microfacet not facing toward the macrosurface, i.e. non heightfield profile of microsurface) ) - const bool valid = !transmitted || (VdotL<=-min(orientedEta,rcpOrientedEta) && _cache.NdotH>nbl::hlsl::numeric_limits::min()); - if (valid) - { - // TODO: can we optimize? - retval.VdotH = dot(V,H); - retval.LdotH = dot(L,H); - retval.NdotH2 = retval.NdotH*retval.NdotH; - return true; - } - return false; - } - template - static bool compute( - out IsotropicMicrofacetCache retval, - const surface_interactions::Isotropic interaction, - const LightSample _sample, - const float eta, out float3 H - ) - { - const float NdotV = interaction.NdotV; - const float NdotL = _sample.NdotL; - const bool transmitted = nbl_glsl_isTransmissionPath(NdotV,NdotL); - - float orientedEta, rcpOrientedEta; - const bool backside = nbl_glsl_getOrientedEtas(orientedEta,rcpOrientedEta,NdotV,eta); - - const vec3 V = interaction.V.getDirection(); - const vec3 L = _sample.L; - const float VdotL = dot(V,L); - return nbl_glsl_calcIsotropicMicrofacetCache(_cache,transmitted,V,L,interaction.N,NdotL,VdotL,orientedEta,rcpOrientedEta,H); - } - template - static bool compute( - out IsotropicMicrofacetCache retval, - const surface_interactions::Isotropic interaction, - const LightSample _sample, - const float eta - ) - { - float3 dummy; - return nbl_glsl_calcIsotropicMicrofacetCache(_cache,transmitted,V,L,interaction.N,NdotL,VdotL,orientedEta,rcpOrientedEta,dummy); - } - - bool isValidVNDFMicrofacet(const bool is_bsdf, const bool transmission, const float VdotL, const float eta, const float rcp_eta) - { - return NdotH >= 0.0 && !(is_bsdf && transmission && (VdotL > -min(eta,rcp_eta))); - } - - float VdotH; - float LdotH; - float NdotH; - float NdotH2; + // always valid because its specialized for the reflective case + static this_t createForReflection(const scalar_type NdotV, const scalar_type NdotL, const scalar_type VdotL, NBL_REF_ARG(scalar_type) LplusV_rcpLen) + { + LplusV_rcpLen = rsqrt(2.0 + 2.0 * VdotL); + + this_t retval; + + retval.VdotH = LplusV_rcpLen * VdotL + LplusV_rcpLen; + retval.LdotH = retval.VdotH; + retval.NdotH = (NdotL + NdotV) * LplusV_rcpLen; + retval.NdotH2 = retval.NdotH * retval.NdotH; + + return retval; + } + static this_t createForReflection(const scalar_type NdotV, const scalar_type NdotL, const scalar_type VdotL) + { + float dummy; + return createForReflection(NdotV, NdotL, VdotL, dummy); + } + template && LightSample) + static this_t createForReflection( + NBL_CONST_REF_ARG(IsotropicInteraction) interaction, + NBL_CONST_REF_ARG(LS) _sample) + { + return createForReflection(interaction.getNdotV(), _sample.getNdotL(), _sample.getVdotL()); + } + + // transmissive cases need to be checked if the path is valid before usage + static this_t create( + NBL_CONST_REF_ARG(vector3_type) V, NBL_CONST_REF_ARG(vector3_type) L, NBL_CONST_REF_ARG(vector3_type) N, + NBL_CONST_REF_ARG(fresnel::OrientedEtas) orientedEtas, NBL_REF_ARG(vector3_type) H) + { + this_t retval; + const scalar_type NdotV = hlsl::dot(N, V); + const scalar_type NdotL = hlsl::dot(N, L); + const scalar_type VdotL = hlsl::dot(V, L); + const bool transmitted = ComputeMicrofacetNormal::isTransmissionPath(NdotV,NdotL); + + ComputeMicrofacetNormal computeMicrofacetNormal = ComputeMicrofacetNormal::create(V,L,N,1.0); + computeMicrofacetNormal.orientedEta.value = orientedEtas.value; + computeMicrofacetNormal.orientedEta.rcp = orientedEtas.rcp; + H = computeMicrofacetNormal.normalized(transmitted); + retval.NdotH = hlsl::dot(N, H); + + // not coming from the medium (reflected) OR + // exiting at the macro scale AND ( (not L outside the cone of possible directions given IoR with constraint VdotH*LdotH<0.0) OR (microfacet not facing toward the macrosurface, i.e. non heightfield profile of microsurface) ) + const bool valid = !transmitted || (VdotL <= -hlsl::min(orientedEtas.value, orientedEtas.rcp) && retval.NdotH >= nbl::hlsl::numeric_limits::min); + if (valid) + { + retval.VdotH = hlsl::dot(V,H); + retval.LdotH = hlsl::dot(L,H); + retval.NdotH2 = retval.NdotH * retval.NdotH; + } + else + retval.NdotH = -1.0; + return retval; + } + + template && LightSample) + static this_t create( + NBL_CONST_REF_ARG(IsotropicInteraction) interaction, + NBL_CONST_REF_ARG(LS) _sample, + NBL_CONST_REF_ARG(fresnel::OrientedEtas) orientedEtas, NBL_REF_ARG(vector3_type) H) + { + const vector3_type V = interaction.getV().getDirection(); + const vector3_type L = _sample.getL().getDirection(); + + return create(V,L,interaction.getN(),orientedEtas,H); + } + + bool isValidVNDFMicrofacet(const bool is_bsdf, const bool transmission, const scalar_type VdotL, const scalar_type eta, const scalar_type rcp_eta) + { + return NdotH >= 0.0 && !(is_bsdf && transmission && (VdotL > -hlsl::min(eta, rcp_eta))); + } + + scalar_type getVdotH() NBL_CONST_MEMBER_FUNC + { + assert(VdotH >= scalar_type(0.0)); + return VdotH; + } + + scalar_type getLdotH() NBL_CONST_MEMBER_FUNC { return LdotH; } + scalar_type getNdotH() NBL_CONST_MEMBER_FUNC { return NdotH; } + scalar_type getNdotH2() NBL_CONST_MEMBER_FUNC { return NdotH2; } + + scalar_type VdotH; + scalar_type LdotH; + scalar_type NdotH; + scalar_type NdotH2; }; -struct AnisotropicMicrofacetCache : IsotropicMicrofacetCache + +#define NBL_CONCEPT_NAME AnisotropicMicrofacetCache +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (cache, T) +#define NBL_CONCEPT_PARAM_1 (aniso, surface_interactions::SAnisotropic > >) +#define NBL_CONCEPT_PARAM_2 (pNdotL, typename T::scalar_type) +#define NBL_CONCEPT_PARAM_3 (_sample, SLightSample >) +#define NBL_CONCEPT_PARAM_4 (V, typename T::vector3_type) +#define NBL_CONCEPT_PARAM_5 (b0, bool) +#define NBL_CONCEPT_PARAM_6 (eta, fresnel::OrientedEtas) +NBL_CONCEPT_BEGIN(7) +#define cache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define pNdotL NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +#define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +#define V NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 +#define b0 NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 +#define eta NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_6 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::isocache_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((cache.getTdotH()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((cache.getBdotH()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(V,V)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(V,V,b0,pNdotL,pNdotL)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::createForReflection(V,V,pNdotL)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template createForReflection > >,SLightSample > >(aniso,_sample)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(V,V,V,V,V,eta,V)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template create > >,SLightSample > >(aniso,_sample,eta)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(CreatableIsotropicMicrofacetCache, typename T::isocache_type)) +); +#undef eta +#undef b0 +#undef V +#undef _sample +#undef pNdotL +#undef aniso +#undef cache +#include + +template) +struct SAnisotropicMicrofacetCache { - // always valid by construction - static AnisotropicMicrofacetCache create(const float3 tangentSpaceV, const float3 tangentSpaceH) - { - AnisotropicMicrofacetCache retval; - - retval.VdotH = dot(tangentSpaceV,tangentSpaceH); - retval.LdotH = retval.VdotH; - retval.NdotH = tangentSpaceH.z; - retval.NdotH2 = retval.NdotH*retval.NdotH; - retval.TdotH = tangentSpaceH.x; - retval.BdotH = tangentSpaceH.y; - - return retval; - } - static AnisotropicMicrofacetCache create( - const float3 tangentSpaceV, - const float3 tangentSpaceH, - const bool transmitted, - const float rcpOrientedEta, - const float rcpOrientedEta2 - ) - { - AnisotropicMicrofacetCache retval = create(tangentSpaceV,tangentSpaceH); - if (transmitted) - { - const float VdotH = retval.VdotH; - LdotH = transmitted ? refract_compute_NdotT(VdotH<0.0,VdotH*VdotH,rcpOrientedEta2); + using this_t = SAnisotropicMicrofacetCache; + using isocache_type = IsoCache; + using scalar_type = typename IsoCache::scalar_type; + using vector3_type = vector; + using matrix3x3_type = matrix; + + // always valid by construction + static this_t create(NBL_CONST_REF_ARG(vector3_type) tangentSpaceV, NBL_CONST_REF_ARG(vector3_type) tangentSpaceH) + { + this_t retval; + + retval.iso_cache.VdotH = nbl::hlsl::dot(tangentSpaceV,tangentSpaceH); + retval.iso_cache.LdotH = retval.iso_cache.getVdotH(); + retval.iso_cache.NdotH = tangentSpaceH.z; + retval.iso_cache.NdotH2 = retval.iso_cache.getNdotH() * retval.iso_cache.getNdotH(); + retval.TdotH = tangentSpaceH.x; + retval.BdotH = tangentSpaceH.y; + + return retval; } - - return retval; - } - // always valid because its specialized for the reflective case - static AnisotropicMicrofacetCache createForReflection(const float3 tangentSpaceV, const float3 tangentSpaceL, const float VdotL) - { - AnisotropicMicrofacetCache retval; - - float LplusV_rcpLen; - retval = createForReflection(tangentSpaceV.z,tangentSpaceL.z,VdotL,LplusV_rcpLen); - retval.TdotH = (tangentSpaceV.x+tangentSpaceL.x)*LplusV_rcpLen; - retval.BdotH = (tangentSpaceV.y+tangentSpaceL.y)*LplusV_rcpLen; - - return retval; - } - template - static AnisotropicMicrofacetCache createForReflection( - const surface_interactions::Anisotropic interaction, - const LightSample _sample) - { - return createForReflection(interaction.getTangentSpaceV(),_sample.getTangentSpaceL(),_sample.VdotL); - } - // transmissive cases need to be checked if the path is valid before usage - static bool compute( - out AnisotropicMicrofacetCache retval, - const bool transmitted, const float3 V, const float3 L, - const float3 T, const float3 B, const float3 N, - const float NdotL, const float VdotL, - const float orientedEta, const float rcpOrientedEta, out float3 H - ) - { - float3 H; - const bool valid = IsotropicMicrofacetCache::compute(retval,transmitted,V,L,N,NdotL,VdotL,orientedEta,rcpOrientedEta,H); - if (valid) - { - retval.TdotH = dot(T,H); - retval.BdotH = dot(B,H); - } - return valid; - } - template - static bool compute( - out AnisotropicMicrofacetCache retval, - const surface_interactions::Anisotropic interaction, - const LightSample _sample, - const float eta - ) - { - float3 H; - const bool valid = IsotropicMicrofacetCache::compute(retval,interaction,_sample,eta,H); - if (valid) - { - retval.TdotH = dot(interaction.T,H); - retval.BdotH = dot(interaction.B,H); - } - return valid; - } - - float TdotH; - float BdotH; + static this_t create( + NBL_CONST_REF_ARG(vector3_type) tangentSpaceV, + NBL_CONST_REF_ARG(vector3_type) tangentSpaceH, + const bool transmitted, + const scalar_type rcpOrientedEta, + const scalar_type rcpOrientedEta2 + ) + { + this_t retval = create(tangentSpaceV,tangentSpaceH); + if (transmitted) + { + const scalar_type VdotH = retval.iso_cache.VdotH; + retval.iso_cache.LdotH = transmitted ? refract_compute_NdotT(VdotH<0.0,VdotH*VdotH,rcpOrientedEta2) : VdotH; + } + + return retval; + } + // always valid because its specialized for the reflective case + static this_t createForReflection(NBL_CONST_REF_ARG(vector3_type) tangentSpaceV, NBL_CONST_REF_ARG(vector3_type) tangentSpaceL, const scalar_type VdotL) + { + this_t retval; + + scalar_type LplusV_rcpLen; + retval.iso_cache = isocache_type::createForReflection(tangentSpaceV.z, tangentSpaceL.z, VdotL, LplusV_rcpLen); + retval.TdotH = (tangentSpaceV.x + tangentSpaceL.x) * LplusV_rcpLen; + retval.BdotH = (tangentSpaceV.y + tangentSpaceL.y) * LplusV_rcpLen; + + return retval; + } + template && LightSample) + static this_t createForReflection( + NBL_CONST_REF_ARG(AnisotropicInteraction) interaction, + NBL_CONST_REF_ARG(LS) _sample) + { + return createForReflection(interaction.getTangentSpaceV(), _sample.getTangentSpaceL(), _sample.getVdotL()); + } + // transmissive cases need to be checked if the path is valid before usage + static this_t create( + NBL_CONST_REF_ARG(vector3_type) V, NBL_CONST_REF_ARG(vector3_type) L, + NBL_CONST_REF_ARG(vector3_type) T, NBL_CONST_REF_ARG(vector3_type) B, NBL_CONST_REF_ARG(vector3_type) N, + NBL_CONST_REF_ARG(fresnel::OrientedEtas) orientedEtas, NBL_REF_ARG(vector3_type) H + ) + { + this_t retval; + retval.iso_cache = isocache_type::create(V,L,N,orientedEtas,H); + const bool valid = retval.iso_cache.NdotH >= 0.0; + if (valid) + { + retval.TdotH = nbl::hlsl::dot(T,H); + retval.BdotH = nbl::hlsl::dot(B,H); + } + return retval; + } + template && LightSample) + static this_t create( + NBL_CONST_REF_ARG(AnisotropicInteraction) interaction, + NBL_CONST_REF_ARG(LS) _sample, + NBL_CONST_REF_ARG(fresnel::OrientedEtas) orientedEtas + ) + { + this_t retval; + vector3_type H; + retval.iso_cache = isocache_type::template create(interaction.isotropic,_sample,orientedEtas,H); + const bool valid = retval.iso_cache.NdotH >= 0.0; + if (valid) + { + retval.TdotH = nbl::hlsl::dot(interaction.getT(),H); + retval.BdotH = nbl::hlsl::dot(interaction.getB(),H); + } + return retval; + } + + bool isValidVNDFMicrofacet(const bool is_bsdf, const bool transmission, const scalar_type VdotL, const scalar_type eta, const scalar_type rcp_eta) + { + return iso_cache.isValidVNDFMicrofacet(is_bsdf, transmission, VdotL, eta, rcp_eta); + } + + scalar_type getVdotH() NBL_CONST_MEMBER_FUNC { return iso_cache.VdotH; } + scalar_type getLdotH() NBL_CONST_MEMBER_FUNC { return iso_cache.LdotH; } + scalar_type getNdotH() NBL_CONST_MEMBER_FUNC { return iso_cache.NdotH; } + scalar_type getNdotH2() NBL_CONST_MEMBER_FUNC { return iso_cache.NdotH2; } + + scalar_type getTdotH() NBL_CONST_MEMBER_FUNC { return TdotH; } + scalar_type getBdotH() NBL_CONST_MEMBER_FUNC { return BdotH; } + + isocache_type iso_cache; + scalar_type TdotH; + scalar_type BdotH; }; -// finally fixed the semantic F-up, value/pdf = quotient not remainder -template -struct quotient_and_pdf +#define NBL_CONCEPT_NAME BxDF +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (bxdf, T) +#define NBL_CONCEPT_PARAM_1 (spec, typename T::spectral_type) +#define NBL_CONCEPT_PARAM_2 (pdf, typename T::scalar_type) +#define NBL_CONCEPT_PARAM_3 (_sample, typename T::sample_type) +#define NBL_CONCEPT_PARAM_4 (iso, typename T::isotropic_interaction_type) +#define NBL_CONCEPT_PARAM_5 (aniso, typename T::anisotropic_interaction_type) +#define NBL_CONCEPT_PARAM_6 (param_iso, typename T::params_isotropic_t) +#define NBL_CONCEPT_PARAM_7 (param_aniso, typename T::params_anisotropic_t) +#define NBL_CONCEPT_PARAM_8 (u, vector) +NBL_CONCEPT_BEGIN(9) +#define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define spec NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define pdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +#define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +#define iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 +#define aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 +#define param_iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_6 +#define param_aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_7 +#define u NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_8 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::isotropic_interaction_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::anisotropic_interaction_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::sample_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::spectral_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::params_isotropic_t)) + ((NBL_CONCEPT_REQ_TYPE)(T::params_anisotropic_t)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval(param_iso)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval(param_aniso)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(iso,u)), ::nbl::hlsl::is_same_v, typename T::sample_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(aniso,u)), ::nbl::hlsl::is_same_v, typename T::sample_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_pdf(param_iso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_pdf(param_aniso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(LightSample, typename T::sample_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(sampling::Spectral, typename T::spectral_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Isotropic, typename T::isotropic_interaction_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Anisotropic, typename T::anisotropic_interaction_type)) +); +#undef u +#undef param_aniso +#undef param_iso +#undef aniso +#undef iso +#undef _sample +#undef pdf +#undef spec +#undef bxdf +#include + +#define NBL_CONCEPT_NAME MicrofacetBxDF +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (bxdf, T) +#define NBL_CONCEPT_PARAM_1 (spec, typename T::spectral_type) +#define NBL_CONCEPT_PARAM_2 (pdf, typename T::scalar_type) +#define NBL_CONCEPT_PARAM_3 (_sample, typename T::sample_type) +#define NBL_CONCEPT_PARAM_4 (iso, typename T::isotropic_interaction_type) +#define NBL_CONCEPT_PARAM_5 (aniso, typename T::anisotropic_interaction_type) +#define NBL_CONCEPT_PARAM_6 (isocache, typename T::isocache_type) +#define NBL_CONCEPT_PARAM_7 (anisocache, typename T::anisocache_type) +#define NBL_CONCEPT_PARAM_8 (param_iso, typename T::params_isotropic_t) +#define NBL_CONCEPT_PARAM_9 (param_aniso, typename T::params_anisotropic_t) +#define NBL_CONCEPT_PARAM_10 (u, vector) +NBL_CONCEPT_BEGIN(11) +#define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define spec NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define pdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +#define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +#define iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 +#define aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 +#define isocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_6 +#define anisocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_7 +#define param_iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_8 +#define param_aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_9 +#define u NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_10 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::isotropic_interaction_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::anisotropic_interaction_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::sample_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::spectral_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::isocache_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::anisocache_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::params_isotropic_t)) + ((NBL_CONCEPT_REQ_TYPE)(T::params_anisotropic_t)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval(param_iso)), ::nbl::hlsl::is_same_v, T::spectral_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval(param_aniso)), ::nbl::hlsl::is_same_v, T::spectral_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(iso,u,isocache)), ::nbl::hlsl::is_same_v, typename T::sample_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(aniso,u,anisocache)), ::nbl::hlsl::is_same_v, typename T::sample_type)) + //((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.template pdf(_sample,iso)), ::nbl::hlsl::is_scalar_v)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_pdf(param_iso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_pdf(param_aniso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(LightSample, typename T::sample_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(sampling::Spectral, typename T::spectral_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(CreatableIsotropicMicrofacetCache, typename T::isocache_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(AnisotropicMicrofacetCache, typename T::anisocache_type)) +); +#undef u +#undef param_aniso +#undef param_iso +#undef anisocache +#undef isocache +#undef aniso +#undef iso +#undef _sample +#undef pdf +#undef spec +#undef bxdf +#include + +enum BxDFClampMode : uint16_t { - quotient_and_pdf create(const SpectralBins _quotient, const float _pdf) - { - quotient_and_pdf retval; - retval.quotient = _quotient; - retval.pdf = _pdf; - return retval; - } - - SpectralBins value() - { - return quotient*pdf; - } - - SpectralBins quotient; - float pdf; + BCM_NONE = 0, + BCM_MAX, + BCM_ABS }; +// unified param struct for calls to BxDF::create +template) +struct SBxDFCreationParams +{ + bool is_aniso; + vector A; // roughness + Spectrum ior0; // source ior + Spectrum ior1; // destination ior + Scalar eta; // in most cases, eta will be calculated from ior0 and ior1; see monochromeEta in pathtracer.hlsl + Spectrum eta2; + Spectrum luminosityContributionHint; +}; } } diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index d3b3543a28..506daa5b61 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -4,10 +4,11 @@ #ifndef _NBL_BUILTIN_HLSL_BXDF_FRESNEL_INCLUDED_ #define _NBL_BUILTIN_HLSL_BXDF_FRESNEL_INCLUDED_ +#include "nbl/builtin/hlsl/ieee754.hlsl" #include "nbl/builtin/hlsl/cpp_compat.hlsl" +#include "nbl/builtin/hlsl/concepts.hlsl" #include "nbl/builtin/hlsl/numbers.hlsl" #include "nbl/builtin/hlsl/vector_utils/vector_traits.hlsl" -#include "nbl/builtin/hlsl/spirv_intrinsics/core.hlsl" namespace nbl { @@ -17,131 +18,354 @@ namespace hlsl namespace bxdf { -namespace impl +namespace fresnel { -template -struct orientedEtas; -template<> -struct orientedEtas +template || is_vector_v) +struct OrientedEtas { - static bool __call(NBL_REF_ARG(float) orientedEta, NBL_REF_ARG(float) rcpOrientedEta, float NdotI, float eta) + using scalar_type = typename vector_traits::scalar_type; + + static OrientedEtas create(scalar_type NdotI, T eta) { - const bool backside = NdotI < 0.0; - const float rcpEta = 1.0 / eta; - orientedEta = backside ? rcpEta : eta; - rcpOrientedEta = backside ? eta : rcpEta; - return backside; + OrientedEtas retval; + retval.backside = NdotI < scalar_type(0.0); + const T rcpEta = hlsl::promote(1.0) / eta; + retval.value = retval.backside ? rcpEta : eta; + retval.rcp = retval.backside ? eta : rcpEta; + return retval; } + + T value; + T rcp; + bool backside; }; -template<> -struct orientedEtas +template || is_vector_v) +struct OrientedEtaRcps { - static bool __call(NBL_REF_ARG(float32_t3) orientedEta, NBL_REF_ARG(float32_t3) rcpOrientedEta, float NdotI, float32_t3 eta) + using scalar_type = typename vector_traits::scalar_type; + + static OrientedEtaRcps create(scalar_type NdotI, T eta) { - const bool backside = NdotI < 0.0; - const float32_t3 rcpEta = (float32_t3)1.0 / eta; - orientedEta = backside ? rcpEta:eta; - rcpOrientedEta = backside ? eta:rcpEta; - return backside; + OrientedEtaRcps retval; + retval.backside = NdotI < scalar_type(0.0); + const T rcpEta = hlsl::promote(1.0) / eta; + retval.value = retval.backside ? eta : rcpEta; + retval.value2 = retval.value * retval.value; + return retval; } -}; -} -template || is_vector_v) -bool getOrientedEtas(NBL_REF_ARG(T) orientedEta, NBL_REF_ARG(T) rcpOrientedEta, scalar_type_t NdotI, T eta) -{ - return impl::orientedEtas::__call(orientedEta, rcpOrientedEta, NdotI, eta); -} + T diffuseFresnelCorrectionFactor() + { + // assert(n*n==n2); + const T n = hlsl::promote(1.0) / value; + const T n2 = hlsl::promote(1.0) / value2; + vector::Dimension> TIR = n < hlsl::promote(1.0); + T invdenum = nbl::hlsl::mix(hlsl::promote(1.0), hlsl::promote(1.0) / (n2 * n2 * (hlsl::promote(554.33) - hlsl::promote(380.7) * n)), TIR); + T num = n * nbl::hlsl::mix(hlsl::promote(0.1921156102251088), n * hlsl::promote(298.25) - hlsl::promote(261.38) * n2 + hlsl::promote(138.43), TIR); + num += nbl::hlsl::mix(hlsl::promote(0.8078843897748912), hlsl::promote(-1.67), TIR); + return num * invdenum; + } + + T value; + T value2; + bool backside; +}; -template ) -T reflect(T I, T N, typename vector_traits::scalar_type NdotI) -{ - return N * 2.0f * NdotI - I; } -template::Dimension == 3) -struct refract +template) +struct Reflect { - using this_t = refract; - using vector_type = T; - using scalar_type = typename vector_traits::scalar_type; + using this_t = Reflect; + using vector_type = vector; + using scalar_type = T; - static this_t create(NBL_CONST_REF_ARG(vector_type) I, NBL_CONST_REF_ARG(vector_type) N, bool backside, scalar_type NdotI, scalar_type NdotI2, scalar_type rcpOrientedEta, scalar_type rcpOrientedEta2) + static this_t create(NBL_CONST_REF_ARG(vector_type) I, NBL_CONST_REF_ARG(vector_type) N, scalar_type NdotI) { this_t retval; retval.I = I; retval.N = N; - retval.backside = backside; retval.NdotI = NdotI; - retval.NdotI2 = NdotI2; - retval.rcpOrientedEta = rcpOrientedEta; - retval.rcpOrientedEta2 = rcpOrientedEta2; return retval; } - static this_t create(NBL_CONST_REF_ARG(vector_type) I, NBL_CONST_REF_ARG(vector_type) N, scalar_type NdotI, scalar_type eta) + static this_t create(NBL_CONST_REF_ARG(vector_type) I, NBL_CONST_REF_ARG(vector_type) N) { this_t retval; retval.I = I; retval.N = N; - scalar_type orientedEta; - retval.backside = getOrientedEtas(orientedEta, retval.rcpOrientedEta, NdotI, eta); - retval.NdotI = NdotI; - retval.NdotI2 = NdotI * NdotI; - retval.rcpOrientedEta2 = retval.rcpOrientedEta * retval.rcpOrientedEta; + retval.NdotI = computeNdotI(I, N); return retval; } - static this_t create(NBL_CONST_REF_ARG(vector_type) I, NBL_CONST_REF_ARG(vector_type) N, scalar_type eta) + void recomputeNdotI() + { + NdotI = hlsl::dot(N, I); + } + + vector_type operator()() + { + return N * 2.0f * NdotI - I; + } + + vector_type I; + vector_type N; + scalar_type NdotI; +}; + +template) +struct Refract +{ + using this_t = Refract; + using vector_type = vector; + using scalar_type = T; + + static this_t create(NBL_CONST_REF_ARG(fresnel::OrientedEtaRcps) rcpEtas, NBL_CONST_REF_ARG(vector_type) I, NBL_CONST_REF_ARG(vector_type) N) { this_t retval; retval.I = I; retval.N = N; - retval.NdotI = dot(N, I); - scalar_type orientedEta; - retval.backside = getOrientedEtas(orientedEta, retval.rcpOrientedEta, retval.NdotI, eta); + retval.NdotI = hlsl::dot(N, I); retval.NdotI2 = retval.NdotI * retval.NdotI; - retval.rcpOrientedEta2 = retval.rcpOrientedEta * retval.rcpOrientedEta; + retval.rcpOrientedEta = rcpEtas; + retval.recomputeNdotT(rcpEtas.backside, retval.NdotI2, rcpEtas.value2); return retval; } - static scalar_type computeNdotT(bool backside, scalar_type NdotI2, scalar_type rcpOrientedEta2) + static this_t create(NBL_CONST_REF_ARG(fresnel::OrientedEtaRcps) rcpEtas, NBL_CONST_REF_ARG(vector_type) I, NBL_CONST_REF_ARG(vector_type) N, const scalar_type NdotI) { - scalar_type NdotT2 = rcpOrientedEta2 * NdotI2 + 1.0 - rcpOrientedEta2; - scalar_type absNdotT = sqrt(NdotT2); - return backside ? absNdotT : -(absNdotT); + this_t retval; + retval.I = I; + retval.N = N; + retval.NdotI = NdotI; + retval.NdotI2 = retval.NdotI * retval.NdotI; + retval.rcpOrientedEta = rcpEtas; + retval.recomputeNdotT(rcpEtas.backside, retval.NdotI2, rcpEtas.value2); + return retval; } - vector_type doRefract() + void recomputeNdotI() { - return N * (NdotI * rcpOrientedEta + computeNdotT(backside, NdotI2, rcpOrientedEta2)) - rcpOrientedEta * I; + NdotI = hlsl::dot(N, I); + NdotI2 = NdotI * NdotI; } - static vector_type doReflectRefract(bool _refract, NBL_CONST_REF_ARG(vector_type) _I, NBL_CONST_REF_ARG(vector_type) _N, scalar_type _NdotI, scalar_type _NdotTorR, scalar_type _rcpOrientedEta) + void recomputeNdotT(bool backside, scalar_type _NdotI2, scalar_type rcpOrientedEta2) { - return _N * (_NdotI * (_refract ? _rcpOrientedEta : 1.0f) + _NdotTorR) - _I * (_refract ? _rcpOrientedEta : 1.0f); + scalar_type NdotT2 = rcpOrientedEta2 * _NdotI2 + 1.0 - rcpOrientedEta2; + scalar_type absNdotT = sqrt(NdotT2); + NdotT = ieee754::flipSign(absNdotT, backside); } - vector_type doReflectRefract(bool r) + vector_type operator()() { - const T NdotTorR = r ? computeNdotT(backside, NdotI2, rcpOrientedEta2) : NdotI; - return doReflectRefract(r, I, N, NdotI, NdotTorR, rcpOrientedEta); + recomputeNdotT(rcpOrientedEta.backside, NdotI2, rcpOrientedEta.value2); + return N * (NdotI * rcpOrientedEta.value + NdotT) - rcpOrientedEta.value * I; } vector_type I; vector_type N; - bool backside; + scalar_type NdotT; scalar_type NdotI; scalar_type NdotI2; + fresnel::OrientedEtaRcps rcpOrientedEta; +}; + +template) +struct ReflectRefract +{ + using this_t = ReflectRefract; + using vector_type = vector; + using scalar_type = T; + + static this_t create(bool r, NBL_CONST_REF_ARG(vector_type) I, NBL_CONST_REF_ARG(vector_type) N, scalar_type NdotI, scalar_type NdotTorR, scalar_type rcpOrientedEta) + { + this_t retval; + retval.I = I; + retval.N = N; + retval.NdotI = NdotI; + retval.NdotTorR = NdotTorR; + retval.rcpOrientedEta = rcpOrientedEta; + return retval; + } + + static this_t create(bool r, NBL_CONST_REF_ARG(Refract) refract) + { + this_t retval; + retval.I = refract.I; + retval.N = refract.N; + retval.NdotI = refract.NdotI; + retval.NdotTorR = hlsl::mix(refract.NdotI, refract.NdotT, r); + retval.rcpOrientedEta = refract.rcpOrientedEta.value; + return retval; + } + + // when you know you'll reflect + void recomputeNdotR() + { + refract.recomputeNdotI(); + } + + // when you know you'll refract + void recomputeNdotT(bool backside, scalar_type _NdotI2, scalar_type rcpOrientedEta2) + { + refract.recomputeNdotT(backside, _NdotI2, rcpOrientedEta2); + } + + vector_type operator()(const bool doRefract) + { + return N * (NdotI * (hlsl::mix(1.0f, rcpOrientedEta, doRefract)) + NdotTorR) - I * (hlsl::mix(1.0f, rcpOrientedEta, doRefract)); + } + + Refract refract; + vector_type I; + vector_type N; + scalar_type NdotI; + scalar_type NdotTorR; scalar_type rcpOrientedEta; - scalar_type rcpOrientedEta2; }; + +namespace fresnel +{ + +template || is_vector_v) +struct Schlick +{ + using scalar_type = typename vector_traits::scalar_type; + + static Schlick create(NBL_CONST_REF_ARG(T) F0, scalar_type clampedCosTheta) + { + Schlick retval; + retval.F0 = F0; + retval.clampedCosTheta = clampedCosTheta; + return retval; + } + + T operator()() + { + assert(clampedCosTheta > scalar_type(0.0)); + assert(hlsl::promote(0.02) < F0 && F0 <= hlsl::promote(1.0)); + T x = 1.0 - clampedCosTheta; + return F0 + (1.0 - F0) * x*x*x*x*x; + } + + T F0; + scalar_type clampedCosTheta; +}; + +template || is_vector_v) +struct Conductor +{ + using scalar_type = typename vector_traits::scalar_type; + + static Conductor create(NBL_CONST_REF_ARG(T) eta, NBL_CONST_REF_ARG(T) etak, scalar_type clampedCosTheta) + { + Conductor retval; + retval.eta = eta; + retval.etak2 = etak*etak; + retval.clampedCosTheta = clampedCosTheta; + return retval; + } + + T operator()() + { + const scalar_type cosTheta2 = clampedCosTheta * clampedCosTheta; + //const float sinTheta2 = 1.0 - cosTheta2; + + const T etaLen2 = eta * eta + etak2; + assert(hlsl::any(etaLen2 > hlsl::promote(hlsl::exp2(-numeric_limits::digits)))); + const T etaCosTwice = eta * clampedCosTheta * 2.0f; + + const T rs_common = etaLen2 + (T)(cosTheta2); + const T rs2 = (rs_common - etaCosTwice) / (rs_common + etaCosTwice); + + const T rp_common = etaLen2 * cosTheta2 + (T)(1.0); + const T rp2 = (rp_common - etaCosTwice) / (rp_common + etaCosTwice); + + return (rs2 + rp2) * 0.5f; + } + + T eta; + T etak2; + scalar_type clampedCosTheta; +}; + +template || is_vector_v) +struct Dielectric +{ + using scalar_type = typename vector_traits::scalar_type; + + static Dielectric create(NBL_CONST_REF_ARG(T) eta, scalar_type cosTheta) + { + Dielectric retval; + scalar_type absCosTheta = hlsl::abs(cosTheta); + OrientedEtas orientedEta = OrientedEtas::create(absCosTheta, eta); + retval.orientedEta2 = orientedEta.value * orientedEta.value; + retval.absCosTheta = absCosTheta; + return retval; + } + + static T __call(NBL_CONST_REF_ARG(T) orientedEta2, scalar_type absCosTheta) + { + const scalar_type sinTheta2 = 1.0 - absCosTheta * absCosTheta; + + // the max() clamping can handle TIR when orientedEta2<1.0 + const T t0 = hlsl::sqrt(hlsl::max(orientedEta2 - sinTheta2, hlsl::promote(0.0))); + const T rs = (hlsl::promote(absCosTheta) - t0) / (hlsl::promote(absCosTheta) + t0); + + const T t2 = orientedEta2 * absCosTheta; + const T rp = (t0 - t2) / (t0 + t2); + + return (rs * rs + rp * rp) * 0.5f; + } + + T operator()() + { + return __call(orientedEta2, absCosTheta); + } + + T orientedEta2; + scalar_type absCosTheta; +}; + +template || is_vector_v) +struct DielectricFrontFaceOnly +{ + using scalar_type = typename vector_traits::scalar_type; + + static DielectricFrontFaceOnly create(NBL_CONST_REF_ARG(T) orientedEta2, scalar_type absCosTheta) + { + Dielectric retval; + retval.orientedEta2 = orientedEta2; + retval.absCosTheta = hlsl::abs(absCosTheta); + return retval; + } + + T operator()() + { + return Dielectric::__call(orientedEta2, absCosTheta); + } + + T orientedEta2; + scalar_type absCosTheta; +}; + + +// gets the sum of all R, T R T, T R^3 T, T R^5 T, ... paths +template) +T thinDielectricInfiniteScatter(const T singleInterfaceReflectance) +{ + const T doubleInterfaceReflectance = singleInterfaceReflectance * singleInterfaceReflectance; + return hlsl::mix(hlsl::promote(1.0), (singleInterfaceReflectance - doubleInterfaceReflectance) / (hlsl::promote(1.0) - doubleInterfaceReflectance) * 2.0f, doubleInterfaceReflectance > hlsl::promote(0.9999)); +} + } +} } } diff --git a/include/nbl/builtin/hlsl/bxdf/geom_smith.hlsl b/include/nbl/builtin/hlsl/bxdf/geom_smith.hlsl new file mode 100644 index 0000000000..b578ec40b9 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/geom_smith.hlsl @@ -0,0 +1,398 @@ +// Copyright (C) 2018-2023 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_BXDF_GEOM_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_GEOM_INCLUDED_ + +#include "nbl/builtin/hlsl/bxdf/ndf.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace smith +{ + +namespace brdf +{ + +template +struct VNDF_pdf; + +template +struct VNDF_pdf > +{ + using scalar_type = T; + using ndf_type = ndf::Beckmann; + using this_t = VNDF_pdf; + + static this_t create(scalar_type ndf, scalar_type maxNdotV) + { + this_t retval; + retval.ndf = ndf; + retval.maxNdotV = maxNdotV; + return retval; + } + + scalar_type operator()(scalar_type lambda_V) + { + onePlusLambda_V = 1.0 + lambda_V; + ndf::microfacet_to_light_measure_transform transform = ndf::microfacet_to_light_measure_transform::create(ndf / onePlusLambda_V, maxNdotV); + return transform(); + } + + scalar_type ndf; + scalar_type maxNdotV; + scalar_type onePlusLambda_V; +}; + +template +struct VNDF_pdf > +{ + using scalar_type = T; + using ndf_type = ndf::GGX; + using this_t = VNDF_pdf; + + static this_t create(scalar_type ndf, scalar_type maxNdotV) + { + this_t retval; + retval.ndf = ndf; + retval.maxNdotV = maxNdotV; + return retval; + } + + scalar_type operator()(scalar_type G1_over_2NdotV) + { + return ndf * 0.5 * G1_over_2NdotV; + } + + scalar_type ndf; + scalar_type maxNdotV; + scalar_type onePlusLambda_V; +}; + +} + +namespace bsdf +{ + +template) +struct FVNDF_pdf +{ + static FVNDF_pdf create(T fresnel_ndf, T absNdotV) + { + FVNDF_pdf retval; + retval.fresnel_ndf = fresnel_ndf; + retval.absNdotV = absNdotV; + return retval; + } + + T operator()(T G1_over_2NdotV, bool transmitted, T VdotH, T LdotH, T VdotHLdotH, T orientedEta) + { + T FNG = fresnel_ndf * G1_over_2NdotV; + T factor = 0.5; + if (transmitted) + { + const T VdotH_etaLdotH = (VdotH + orientedEta * LdotH); + // VdotHLdotH is negative under transmission, so this factor is negative + factor *= -2.0 * VdotHLdotH / (VdotH_etaLdotH * VdotH_etaLdotH); + } + return FNG * factor; + } + + T fresnel_ndf; + T absNdotV; +}; + +template +struct VNDF_pdf; + +template +struct VNDF_pdf > +{ + using scalar_type = T; + using ndf_type = ndf::Beckmann; + using this_t = VNDF_pdf; + + static this_t create(scalar_type ndf, scalar_type absNdotV) + { + this_t retval; + retval.ndf = ndf; + retval.absNdotV = absNdotV; + return retval; + } + + scalar_type operator()(scalar_type lambda_V, bool transmitted, scalar_type VdotH, scalar_type LdotH, scalar_type VdotHLdotH, scalar_type orientedEta, scalar_type reflectance) + { + onePlusLambda_V = 1.0 + lambda_V; + ndf::microfacet_to_light_measure_transform transform + = ndf::microfacet_to_light_measure_transform::create(hlsl::mix(reflectance, scalar_type(1.0) - reflectance, transmitted) * ndf / onePlusLambda_V, absNdotV, transmitted, VdotH, LdotH, VdotHLdotH, orientedEta); + return transform(); + } + + scalar_type ndf; + scalar_type absNdotV; + scalar_type onePlusLambda_V; +}; + +template +struct VNDF_pdf > +{ + using scalar_type = T; + using ndf_type = ndf::GGX; + using this_t = VNDF_pdf; + + static this_t create(scalar_type ndf, scalar_type absNdotV) + { + this_t retval; + retval.ndf = ndf; + retval.absNdotV = absNdotV; + return retval; + } + + scalar_type operator()(scalar_type G1_over_2NdotV, bool transmitted, scalar_type VdotH, scalar_type LdotH, scalar_type VdotHLdotH, scalar_type orientedEta, scalar_type reflectance) + { + scalar_type FN = hlsl::mix(reflectance, scalar_type(1.0) - reflectance, transmitted) * ndf; + FVNDF_pdf fvndf = FVNDF_pdf::create(FN, absNdotV); + return fvndf(G1_over_2NdotV, transmitted, VdotH, LdotH, VdotHLdotH, orientedEta); + } + + scalar_type ndf; + scalar_type absNdotV; + scalar_type onePlusLambda_V; +}; + +} + + +template) +struct SIsotropicParams +{ + using this_t = SIsotropicParams; + + static this_t create(T a2, T NdotV2, T NdotL2, T lambdaV_plus_one) // beckmann + { + this_t retval; + retval.a2 = a2; + retval.NdotV2 = NdotV2; + retval.NdotL2 = NdotL2; + retval.lambdaV_plus_one = lambdaV_plus_one; + return retval; + } + + static this_t create(T a2, T NdotV, T NdotV2, T NdotL, T NdotL2) // ggx + { + this_t retval; + retval.a2 = a2; + retval.NdotV = NdotV; + retval.NdotV2 = NdotV2; + retval.NdotL = NdotL; + retval.NdotL2 = NdotL2; + retval.one_minus_a2 = 1.0 - a2; + return retval; + } + + T a2; + T NdotV; + T NdotL; + T NdotV2; + T NdotL2; + T lambdaV_plus_one; + T one_minus_a2; +}; + +template) +struct SAnisotropicParams +{ + using this_t = SAnisotropicParams; + + static this_t create(T ax2, T ay2, T TdotV2, T BdotV2, T NdotV2, T TdotL2, T BdotL2, T NdotL2, T lambdaV_plus_one) // beckmann + { + this_t retval; + retval.ax2 = ax2; + retval.ay2 = ay2; + retval.TdotV2 = TdotV2; + retval.BdotV2 = BdotV2; + retval.NdotV2 = NdotV2; + retval.TdotL2 = TdotL2; + retval.BdotL2 = BdotL2; + retval.NdotL2 = NdotL2; + retval.lambdaV_plus_one = lambdaV_plus_one; + return retval; + } + + static this_t create(T ax2, T ay2, T NdotV, T TdotV2, T BdotV2, T NdotV2, T NdotL, T TdotL2, T BdotL2, T NdotL2) // ggx + { + this_t retval; + retval.ax2 = ax2; + retval.ay2 = ay2; + retval.NdotL = NdotL; + retval.NdotV = NdotV; + retval.TdotV2 = TdotV2; + retval.BdotV2 = BdotV2; + retval.NdotV2 = NdotV2; + retval.TdotL2 = TdotL2; + retval.BdotL2 = BdotL2; + retval.NdotL2 = NdotL2; + return retval; + } + + T ax2; + T ay2; + T NdotV; + T NdotL; + T TdotV2; + T BdotV2; + T NdotV2; + T TdotL2; + T BdotL2; + T NdotL2; + T lambdaV_plus_one; +}; + + +// beckmann +template) +struct Beckmann +{ + using scalar_type = T; + + scalar_type G1(scalar_type lambda) + { + return 1.0 / (1.0 + lambda); + } + + scalar_type C2(scalar_type NdotX2, scalar_type a2) + { + return NdotX2 / (a2 * (1.0 - NdotX2)); + } + + scalar_type C2(scalar_type TdotX2, scalar_type BdotX2, scalar_type NdotX2, scalar_type ax2, scalar_type ay2) + { + return NdotX2 / (TdotX2 * ax2 + BdotX2 * ay2); + } + + scalar_type Lambda(scalar_type c2) + { + scalar_type c = sqrt(c2); + scalar_type nom = 1.0 - 1.259 * c + 0.396 * c2; + scalar_type denom = 2.181 * c2 + 3.535 * c; + return hlsl::mix(0.0, nom / denom, c < 1.6); + } + + scalar_type Lambda(scalar_type NdotX2, scalar_type a2) + { + return Lambda(C2(NdotX2, a2)); + } + + scalar_type Lambda(scalar_type TdotX2, scalar_type BdotX2, scalar_type NdotX2, scalar_type ax2, scalar_type ay2) + { + return Lambda(C2(TdotX2, BdotX2, NdotX2, ax2, ay2)); + } + + scalar_type correlated(SIsotropicParams params) + { + scalar_type c2 = C2(params.NdotV2, params.a2); + scalar_type L_v = Lambda(c2); + c2 = C2(params.NdotL2, params.a2); + scalar_type L_l = Lambda(c2); + return G1(L_v + L_l); + } + + scalar_type correlated(SAnisotropicParams params) + { + scalar_type c2 = C2(params.TdotV2, params.BdotV2, params.NdotV2, params.ax2, params.ay2); + scalar_type L_v = Lambda(c2); + c2 = C2(params.TdotL2, params.BdotL2, params.NdotL2, params.ax2, params.ay2); + scalar_type L_l = Lambda(c2); + return G1(L_v + L_l); + } + + scalar_type G2_over_G1(SIsotropicParams params) + { + scalar_type lambdaL = Lambda(params.NdotL2, params.a2); + return params.lambdaV_plus_one / (params.lambdaV_plus_one + lambdaL); + } + + scalar_type G2_over_G1(SAnisotropicParams params) + { + scalar_type c2 = C2(params.TdotL2, params.BdotL2, params.NdotL2, params.ax2, params.ay2); + scalar_type lambdaL = Lambda(c2); + return params.lambdaV_plus_one / (params.lambdaV_plus_one + lambdaL); + } +}; + + +// ggx +template) +struct GGX +{ + using scalar_type = T; + + scalar_type devsh_part(scalar_type NdotX2, scalar_type a2, scalar_type one_minus_a2) + { + return sqrt(a2 + one_minus_a2 * NdotX2); + } + + scalar_type devsh_part(scalar_type TdotX2, scalar_type BdotX2, scalar_type NdotX2, scalar_type ax2, scalar_type ay2) + { + return sqrt(TdotX2 * ax2 + BdotX2 * ay2 + NdotX2); + } + + scalar_type G1_wo_numerator(scalar_type NdotX, scalar_type NdotX2, scalar_type a2, scalar_type one_minus_a2) + { + return 1.0 / (NdotX + devsh_part(NdotX2,a2,one_minus_a2)); + } + + scalar_type G1_wo_numerator(scalar_type NdotX, scalar_type TdotX2, scalar_type BdotX2, scalar_type NdotX2, scalar_type ax2, scalar_type ay2) + { + return 1.0 / (NdotX + devsh_part(TdotX2, BdotX2, NdotX2, ax2, ay2)); + } + + scalar_type G1_wo_numerator(scalar_type NdotX, scalar_type devsh_part) + { + return 1.0 / (NdotX + devsh_part); + } + + scalar_type correlated_wo_numerator(SIsotropicParams params) + { + scalar_type Vterm = params.NdotL * devsh_part(params.NdotV2, params.a2, params.one_minus_a2); + scalar_type Lterm = params.NdotV * devsh_part(params.NdotL2, params.a2, params.one_minus_a2); + return 0.5 / (Vterm + Lterm); + } + + scalar_type correlated_wo_numerator(SAnisotropicParams params) + { + scalar_type Vterm = params.NdotL * devsh_part(params.TdotV2, params.BdotV2, params.NdotV2, params.ax2, params.ay2); + scalar_type Lterm = params.NdotV * devsh_part(params.TdotL2, params.BdotL2, params.NdotL2, params.ax2, params.ay2); + return 0.5 / (Vterm + Lterm); + } + + scalar_type G2_over_G1(SIsotropicParams params) + { + scalar_type devsh_v = devsh_part(params.NdotV2, params.a2, params.one_minus_a2); + scalar_type G2_over_G1 = params.NdotL * (devsh_v + params.NdotV); // alternative `Vterm+NdotL*NdotV /// NdotL*NdotV could come as a parameter + G2_over_G1 /= params.NdotV * devsh_part(params.NdotL2, params.a2, params.one_minus_a2) + params.NdotL * devsh_v; + + return G2_over_G1; + } + + scalar_type G2_over_G1(SAnisotropicParams params) + { + scalar_type devsh_v = devsh_part(params.TdotV2, params.BdotV2, params.NdotV2, params.ax2, params.ay2); + scalar_type G2_over_G1 = params.NdotL * (devsh_v + params.NdotV); + G2_over_G1 /= params.NdotV * devsh_part(params.TdotL2, params.BdotL2, params.NdotL2, params.ax2, params.ay2) + params.NdotL * devsh_v; + + return G2_over_G1; + } + +}; + +} +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/bxdf/ndf.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf.hlsl new file mode 100644 index 0000000000..dfaf4f06a1 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/ndf.hlsl @@ -0,0 +1,339 @@ +// Copyright (C) 2018-2023 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_BXDF_NDF_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_NDF_INCLUDED_ + +#include "nbl/builtin/hlsl/limits.hlsl" +#include "nbl/builtin/hlsl/bxdf/common.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace ndf +{ + +template) +struct SIsotropicParams +{ + using this_t = SIsotropicParams; + + static this_t create(T n_or_a2, T NdotH, T NdotH2) // beckmann, ggx : a2; blinn-phong : n + { + this_t retval; + retval.n_or_a2 = n_or_a2; + retval.NdotH = NdotH; + retval.NdotH2 = NdotH2; + return retval; + } + + T n_or_a2; + T NdotH; + T NdotH2; +}; + +template) +struct SAnisotropicParams +{ + using this_t = SAnisotropicParams; + + static this_t create(T NdotH, T one_minus_NdotH2_rcp, T TdotH2, T BdotH2, T nx, T ny) // blinn-phong + { + this_t retval; + retval.NdotH = NdotH; + retval.one_minus_NdotH2_rcp = one_minus_NdotH2_rcp; + retval.TdotH2 = TdotH2; + retval.BdotH2 = BdotH2; + retval.nx = nx; + retval.ny = ny; + return retval; + } + + static this_t create(T ax, T ay, T ax2, T ay2, T TdotH2, T BdotH2, T NdotH2) // beckmann, ggx aniso + { + this_t retval; + retval.ax = ax; + retval.ax2 = ax2; + retval.ay = ay; + retval.ay2 = ay2; + retval.TdotH2 = TdotH2; + retval.BdotH2 = BdotH2; + retval.NdotH2 = NdotH2; + return retval; + } + + static this_t create(T a2, T TdotH, T BdotH, T NdotH) // ggx burley + { + this_t retval; + retval.ax = a2; + retval.TdotH = TdotH; + retval.BdotH = BdotH; + retval.NdotH = NdotH; + return retval; + } + + T ax; + T ay; + T ax2; + T ay2; + T nx; + T ny; + T NdotH; + T TdotH; + T BdotH; + T NdotH2; + T TdotH2; + T BdotH2; + T one_minus_NdotH2_rcp; +}; + + +template) +struct BlinnPhong +{ + using scalar_type = T; + + // blinn-phong + scalar_type operator()(SIsotropicParams params) + { + // n is shininess exponent in original paper + return isinf(params.n_or_a2) ? numeric_limits::infinity : numbers::inv_pi * 0.5 * (params.n_or_a2 + 2.0) * pow(params.NdotH, params.n_or_a2); + } + + //ashikhmin-shirley ndf + scalar_type operator()(SAnisotropicParams params) + { + scalar_type n = (params.TdotH2 * params.ny + params.BdotH2 * params.nx) * params.one_minus_NdotH2_rcp; + return (isinf(params.nx) || isinf(params.ny)) ? numeric_limits::infinity : + sqrt((params.nx + 2.0) * (params.ny + 2.0)) * numbers::inv_pi * 0.5 * pow(params.NdotH, n); + } +}; + +template) +struct Beckmann +{ + using scalar_type = T; + + scalar_type operator()(SIsotropicParams params) + { + scalar_type nom = exp( (params.NdotH2 - 1.0) / (params.n_or_a2 * params.NdotH2) ); // exp(x) == exp2(x/log(2)) ? + scalar_type denom = params.n_or_a2 * params.NdotH2 * params.NdotH2; + return numbers::inv_pi * nom / denom; + } + + scalar_type operator()(SAnisotropicParams params) + { + scalar_type nom = exp(-(params.TdotH2 / params.ax2 + params.BdotH2 / params.ay2) / params.NdotH2); + scalar_type denom = params.ax * params.ay * params.NdotH2 * params.NdotH2; + return numbers::inv_pi * nom / denom; + } +}; + + +template) +struct GGX +{ + using scalar_type = T; + + // trowbridge-reitz + scalar_type operator()(SIsotropicParams params) + { + scalar_type denom = params.NdotH2 * (params.n_or_a2 - 1.0) + 1.0; + return params.n_or_a2 * numbers::inv_pi / (denom * denom); + } + + scalar_type operator()(SAnisotropicParams params) + { + scalar_type a2 = params.ax * params.ay; + scalar_type denom = params.TdotH2 / params.ax2 + params.BdotH2 / params.ay2 + params.NdotH2; + return numbers::inv_pi / (a2 * denom * denom); + } + + // burley + scalar_type operator()(SAnisotropicParams params, scalar_type anisotropy) + { + scalar_type antiAniso = 1.0 - anisotropy; + scalar_type atab = params.ax * antiAniso; + scalar_type anisoTdotH = antiAniso * params.TdotH; + scalar_type anisoNdotH = antiAniso * params.NdotH; + scalar_type w2 = antiAniso/(params.BdotH * params.BdotH + anisoTdotH * anisoTdotH + anisoNdotH * anisoNdotH * params.ax); + return w2 * w2 * atab * numbers::inv_pi; + } +}; + +// common +namespace impl +{ +template +struct is_ggx : bool_constant< + is_same >::value +> {}; +} + +template +struct is_ggx : impl::is_ggx {}; + +template +NBL_CONSTEXPR bool is_ggx_v = is_ggx::value; + + +enum MicrofacetTransformTypes : uint16_t +{ + REFLECT_BIT = 0b01, + REFRACT_BIT = 0b10, + REFLECT_REFRACT_BIT = 0b11 +}; + +template +struct microfacet_to_light_measure_transform; + + +template +struct microfacet_to_light_measure_transform +{ + using this_t = microfacet_to_light_measure_transform; + using scalar_type = typename NDF::scalar_type; + + static this_t create(scalar_type NDFcos, scalar_type maxNdotV) + { + this_t retval; + retval.NDFcos = NDFcos; + if (is_ggx_v) + retval.maxNdotL = maxNdotV; + else + retval.maxNdotV = maxNdotV; + return retval; + } + + scalar_type operator()() + { + if (is_ggx_v) + return NDFcos * maxNdotL; + else + return 0.25 * NDFcos / maxNdotV; + } + + scalar_type NDFcos; + scalar_type maxNdotV; + scalar_type maxNdotL; +}; + +template +struct microfacet_to_light_measure_transform +{ + using this_t = microfacet_to_light_measure_transform; + using scalar_type = typename NDF::scalar_type; + + static this_t create(scalar_type NDFcos, scalar_type absNdotV, scalar_type VdotH, scalar_type LdotH, scalar_type VdotHLdotH, scalar_type orientedEta) + { + this_t retval; + retval.NDFcos = NDFcos; + if (is_ggx_v) + retval.absNdotL = absNdotV; + else + retval.absNdotV = absNdotV; + retval.VdotH = VdotH; + retval.LdotH = LdotH; + retval.VdotHLdotH = VdotHLdotH; + retval.orientedEta = orientedEta; + return retval; + } + + scalar_type operator()() + { + if (is_ggx_v) + { + scalar_type denominator = absNdotL; + const scalar_type VdotH_etaLdotH = (VdotH + orientedEta * LdotH); + // VdotHLdotH is negative under transmission, so thats denominator is negative + denominator *= -4.0 * VdotHLdotH / (VdotH_etaLdotH * VdotH_etaLdotH); + return NDFcos * denominator; + } + else + { + scalar_type denominator = absNdotV; + const scalar_type VdotH_etaLdotH = (VdotH + orientedEta * LdotH); + // VdotHLdotH is negative under transmission, so thats denominator is negative + denominator *= -VdotH_etaLdotH * VdotH_etaLdotH; + return NDFcos * VdotHLdotH / denominator; + } + } + + scalar_type NDFcos; + scalar_type absNdotV; + scalar_type absNdotL; + + scalar_type VdotH; + scalar_type LdotH; + scalar_type VdotHLdotH; + scalar_type orientedEta; +}; + +template +struct microfacet_to_light_measure_transform +{ + using this_t = microfacet_to_light_measure_transform; + using scalar_type = typename NDF::scalar_type; + + static this_t create(scalar_type NDFcos, scalar_type absNdotV, bool transmitted, scalar_type VdotH, scalar_type LdotH, scalar_type VdotHLdotH, scalar_type orientedEta) + { + this_t retval; + retval.NDFcos = NDFcos; + if (is_ggx_v) + retval.absNdotL = absNdotV; + else + retval.absNdotV = absNdotV; + retval.transmitted = transmitted; + retval.VdotH = VdotH; + retval.LdotH = LdotH; + retval.VdotHLdotH = VdotHLdotH; + retval.orientedEta = orientedEta; + return retval; + } + + scalar_type operator()() + { + if (is_ggx_v) + { + scalar_type denominator = absNdotL; + if (transmitted) + { + const scalar_type VdotH_etaLdotH = (VdotH + orientedEta * LdotH); + // VdotHLdotH is negative under transmission, so thats denominator is negative + denominator *= -4.0 * VdotHLdotH / (VdotH_etaLdotH * VdotH_etaLdotH); + } + return NDFcos * denominator; + } + else + { + scalar_type denominator = absNdotV; + if (transmitted) + { + const scalar_type VdotH_etaLdotH = (VdotH + orientedEta * LdotH); + // VdotHLdotH is negative under transmission, so thats denominator is negative + denominator *= -VdotH_etaLdotH * VdotH_etaLdotH; + } + return NDFcos * (transmitted ? VdotHLdotH : 0.25) / denominator; + } + } + + bool transmitted; + scalar_type NDFcos; + scalar_type absNdotV; + scalar_type absNdotL; + + scalar_type VdotH; + scalar_type LdotH; + scalar_type VdotHLdotH; + scalar_type orientedEta; +}; + +} +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/bxdf/reflection.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection.hlsl index 878cd7b6f3..ed049d3d1a 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection.hlsl @@ -1,40 +1,22 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h #ifndef _NBL_BUILTIN_HLSL_BXDF_REFLECTION_INCLUDED_ #define _NBL_BUILTIN_HLSL_BXDF_REFLECTION_INCLUDED_ -#include +#include "nbl/builtin/hlsl/bxdf/reflection/lambertian.hlsl" +#include "nbl/builtin/hlsl/bxdf/reflection/oren_nayar.hlsl" +#include "nbl/builtin/hlsl/bxdf/reflection/beckmann.hlsl" +#include "nbl/builtin/hlsl/bxdf/reflection/ggx.hlsl" namespace nbl { namespace hlsl { -namespace bxdf -{ -namespace reflection -{ -template -LightSample cos_generate(const surface_interactions::Isotropic interaction) -{ - return LightSample(interaction.V.reflect(interaction.N,interaction.NdotV),interaction.NdotV,interaction.N); -} -template -LightSample cos_generate(const surface_interactions::Anisotropic interaction) -{ - return LightSample(interaction.V.reflect(interaction.N,interaction.NdotV),interaction.NdotV,interaction.T,interaction.B,interaction.N); -} +// After Clang-HLSL introduces https://en.cppreference.com/w/cpp/language/namespace_alias +// namespace brdf = bxdf::reflection; -// for information why we don't check the relation between `V` and `L` or `N` and `H`, see comments for `nbl::hlsl::transmission::cos_quotient_and_pdf` -template -quotient_and_pdf cos_quotient_and_pdf() -{ - return quotient_and_pdf::create(SpectralBins(1.f),nbl::hlsl::numeric_limits::inf()); -} - -} -} } } diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/beckmann.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/beckmann.hlsl new file mode 100644 index 0000000000..f5f272c9dd --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/reflection/beckmann.hlsl @@ -0,0 +1,392 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_BXDF_REFLECTION_BECKMANN_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_REFLECTION_BECKMANN_INCLUDED_ + +#include "nbl/builtin/hlsl/bxdf/common.hlsl" +#include "nbl/builtin/hlsl/sampling/cos_weighted.hlsl" +#include "nbl/builtin/hlsl/bxdf/geom_smith.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace reflection +{ + +template +struct BeckmannParams; + +template +NBL_PARTIAL_REQ_TOP(!surface_interactions::Anisotropic && !AnisotropicMicrofacetCache) +struct BeckmannParams && !AnisotropicMicrofacetCache) > +{ + using this_t = BeckmannParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, NBL_CONST_REF_ARG(MC) cache, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval.cache = cache; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + Scalar getNdotH() NBL_CONST_MEMBER_FUNC { return cache.getNdotH(); } + Scalar getNdotH2() NBL_CONST_MEMBER_FUNC { return cache.getNdotH2(); } + Scalar getVdotH() NBL_CONST_MEMBER_FUNC { return cache.getVdotH(); } + Scalar getLdotH() NBL_CONST_MEMBER_FUNC { return cache.getLdotH(); } + + LS _sample; + SI interaction; + MC cache; + BxDFClampMode _clamp; +}; +template +NBL_PARTIAL_REQ_TOP(surface_interactions::Anisotropic && AnisotropicMicrofacetCache) +struct BeckmannParams && AnisotropicMicrofacetCache) > +{ + using this_t = BeckmannParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, NBL_CONST_REF_ARG(MC) cache, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval.cache = cache; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + Scalar getNdotH() NBL_CONST_MEMBER_FUNC { return cache.getNdotH(); } + Scalar getNdotH2() NBL_CONST_MEMBER_FUNC { return cache.getNdotH2(); } + Scalar getVdotH() NBL_CONST_MEMBER_FUNC { return cache.getVdotH(); } + Scalar getLdotH() NBL_CONST_MEMBER_FUNC { return cache.getLdotH(); } + + // aniso + Scalar getTdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getTdotL() * _sample.getTdotL(); } + Scalar getBdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getBdotL() * _sample.getBdotL(); } + Scalar getTdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getTdotV() * interaction.getTdotV(); } + Scalar getBdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getBdotV() * interaction.getBdotV(); } + Scalar getTdotH2() NBL_CONST_MEMBER_FUNC {return cache.getTdotH() * cache.getTdotH(); } + Scalar getBdotH2() NBL_CONST_MEMBER_FUNC {return cache.getBdotH() * cache.getBdotH(); } + + LS _sample; + SI interaction; + MC cache; + BxDFClampMode _clamp; +}; + +template && surface_interactions::Isotropic && surface_interactions::Anisotropic && CreatableIsotropicMicrofacetCache && AnisotropicMicrofacetCache) +struct SBeckmannBxDF +{ + using this_t = SBeckmannBxDF; + using scalar_type = typename LS::scalar_type; + using ray_dir_info_type = typename LS::ray_dir_info_type; + using vector2_type = vector; + using vector3_type = vector; + + using isotropic_interaction_type = Iso; + using anisotropic_interaction_type = Aniso; + using sample_type = LS; + using spectral_type = Spectrum; + using quotient_pdf_type = sampling::quotient_and_pdf; + using isocache_type = IsoCache; + using anisocache_type = AnisoCache; + + using params_isotropic_t = BeckmannParams; + using params_anisotropic_t = BeckmannParams; + + + // iso + static this_t create(scalar_type A, NBL_CONST_REF_ARG(spectral_type) ior0, NBL_CONST_REF_ARG(spectral_type) ior1) + { + this_t retval; + retval.A = vector2_type(A,A); + retval.ior0 = ior0; + retval.ior1 = ior1; + return retval; + } + + // aniso + static this_t create(scalar_type ax, scalar_type ay, NBL_CONST_REF_ARG(spectral_type) ior0, NBL_CONST_REF_ARG(spectral_type) ior1) + { + this_t retval; + retval.A = vector2_type(ax,ay); + retval.ior0 = ior0; + retval.ior1 = ior1; + return retval; + } + + static this_t create(NBL_CONST_REF_ARG(SBxDFCreationParams) params) + { + if (params.is_aniso) + return create(params.A.x, params.A.y, params.ior0, params.ior1); + else + return create(params.A.x, params.ior0, params.ior1); + } + + void init(NBL_CONST_REF_ARG(SBxDFCreationParams) params) + { + A = params.A; + ior0 = params.ior0; + ior1 = params.ior1; + } + + scalar_type __eval_DG_wo_clamps(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + scalar_type a2 = A.x*A.x; + ndf::SIsotropicParams ndfparams = ndf::SIsotropicParams::create(a2, params.getNdotH(), params.getNdotH2()); + ndf::Beckmann beckmann_ndf; + scalar_type NG = beckmann_ndf(ndfparams); + if (a2 > numeric_limits::min) + { + smith::SIsotropicParams smithparams = smith::SIsotropicParams::create(a2, params.getNdotV2(), params.getNdotL2(), 0); + smith::Beckmann beckmann_smith; + NG *= beckmann_smith.correlated(smithparams); + } + return NG; + } + scalar_type __eval_DG_wo_clamps(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + const scalar_type ax2 = A.x*A.x; + const scalar_type ay2 = A.y*A.y; + ndf::SAnisotropicParams ndfparams = ndf::SAnisotropicParams::create(A.x, A.y, ax2, ay2, params.getTdotH2(), params.getBdotH2(), params.getNdotH2()); + ndf::Beckmann beckmann_ndf; + scalar_type NG = beckmann_ndf(ndfparams); + if (any >(A > (vector2_type)numeric_limits::min)) + { + smith::SAnisotropicParams smithparams = smith::SAnisotropicParams::create(ax2, ay2, params.getTdotV2(), params.getBdotV2(), params.getNdotV2(), params.getTdotL2(), params.getBdotL2(), params.getNdotL2(), 0); + smith::Beckmann beckmann_smith; + NG *= beckmann_smith.correlated(smithparams); + } + return NG; + } + + spectral_type eval(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + if (params.getNdotVUnclamped() > numeric_limits::min) + { + scalar_type scalar_part = __eval_DG_wo_clamps(params); + ndf::microfacet_to_light_measure_transform,ndf::REFLECT_BIT> microfacet_transform = ndf::microfacet_to_light_measure_transform,ndf::REFLECT_BIT>::create(scalar_part, params.getNdotVUnclamped()); + fresnel::Conductor f = fresnel::Conductor::create(ior0, ior1, params.getVdotH()); + return f() * microfacet_transform(); + } + else + return (spectral_type)0.0; + } + spectral_type eval(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + if (params.getNdotVUnclamped() > numeric_limits::min) + { + scalar_type scalar_part = __eval_DG_wo_clamps(params); + ndf::microfacet_to_light_measure_transform,ndf::REFLECT_BIT> microfacet_transform = ndf::microfacet_to_light_measure_transform,ndf::REFLECT_BIT>::create(scalar_part, params.getNdotVUnclamped()); + fresnel::Conductor f = fresnel::Conductor::create(ior0, ior1, params.getVdotH()); + return f() * microfacet_transform(); + } + else + return (spectral_type)0.0; + } + + vector3_type __generate(NBL_CONST_REF_ARG(vector3_type) localV, NBL_CONST_REF_ARG(vector2_type) u) + { + //stretch + vector3_type V = nbl::hlsl::normalize(vector3_type(A.x * localV.x, A.y * localV.y, localV.z)); + + vector2_type slope; + if (V.z > 0.9999)//V.z=NdotV=cosTheta in tangent space + { + scalar_type r = sqrt(-log(1.0 - u.x)); + scalar_type sinPhi = sin(2.0 * numbers::pi * u.y); + scalar_type cosPhi = cos(2.0 * numbers::pi * u.y); + slope = (vector2_type)r * vector2_type(cosPhi,sinPhi); + } + else + { + scalar_type cosTheta = V.z; + scalar_type sinTheta = sqrt(1.0 - cosTheta * cosTheta); + scalar_type tanTheta = sinTheta / cosTheta; + scalar_type cotTheta = 1.0 / tanTheta; + + scalar_type a = -1.0; + scalar_type c = erf(cosTheta); + scalar_type sample_x = max(u.x, 1.0e-6); + scalar_type theta = acos(cosTheta); + scalar_type fit = 1.0 + theta * (-0.876 + theta * (0.4265 - 0.0594*theta)); + scalar_type b = c - (1.0 + c) * pow(1.0-sample_x, fit); + + scalar_type normalization = 1.0 / (1.0 + c + numbers::inv_sqrtpi * tanTheta * exp(-cosTheta*cosTheta)); + + const int ITER_THRESHOLD = 10; + const float MAX_ACCEPTABLE_ERR = 1.0e-5; + int it = 0; + float value=1000.0; + while (++it < ITER_THRESHOLD && nbl::hlsl::abs(value) > MAX_ACCEPTABLE_ERR) + { + if (!(b >= a && b <= c)) + b = 0.5 * (a + c); + + float invErf = erfInv(b); + value = normalization * (1.0 + b + numbers::inv_sqrtpi * tanTheta * exp(-invErf * invErf)) - sample_x; + float derivative = normalization * (1.0 - invErf * cosTheta); + + if (value > 0.0) + c = b; + else + a = b; + + b -= value/derivative; + } + // TODO: investigate if we can replace these two erf^-1 calls with a box muller transform + slope.x = erfInv(b); + slope.y = erfInv(2.0 * max(u.y, 1.0e-6) - 1.0); + } + + scalar_type sinTheta = sqrt(1.0 - V.z*V.z); + scalar_type cosPhi = sinTheta==0.0 ? 1.0 : clamp(V.x/sinTheta, -1.0, 1.0); + scalar_type sinPhi = sinTheta==0.0 ? 0.0 : clamp(V.y/sinTheta, -1.0, 1.0); + //rotate + scalar_type tmp = cosPhi*slope.x - sinPhi*slope.y; + slope.y = sinPhi*slope.x + cosPhi*slope.y; + slope.x = tmp; + + //unstretch + slope = vector2_type(A.x,A.y)*slope; + + return nbl::hlsl::normalize(vector3_type(-slope, 1.0)); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(vector2_type) u, NBL_REF_ARG(anisocache_type) cache) + { + const vector3_type localV = interaction.getTangentSpaceV(); + const vector3_type H = __generate(localV, u); + + cache = anisocache_type::create(localV, H); + ray_dir_info_type localL; + bxdf::Reflect r = bxdf::Reflect::create(localV, H, cache.iso_cache.getVdotH()); + localL.direction = r(); + + return sample_type::createFromTangentSpace(localV, localL, interaction.getFromTangentSpace()); + } + + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(vector2_type) u, NBL_REF_ARG(isocache_type) cache) + { + anisocache_type anisocache; + sample_type s = generate(anisotropic_interaction_type::create(interaction), u, anisocache); + cache = anisocache.iso_cache; + return s; + } + + scalar_type pdf(NBL_CONST_REF_ARG(params_isotropic_t) params, NBL_REF_ARG(scalar_type) onePlusLambda_V) + { + scalar_type ndf, lambda; + scalar_type a2 = A.x*A.x; + ndf::SIsotropicParams ndfparams = ndf::SIsotropicParams::create(a2, params.getNdotH(), params.getNdotH2()); + ndf::Beckmann beckmann_ndf; + ndf = beckmann_ndf(ndfparams); + + smith::Beckmann beckmann_smith; + lambda = beckmann_smith.Lambda(params.getNdotV2(), a2); + + smith::brdf::VNDF_pdf > vndf = smith::brdf::VNDF_pdf >::create(ndf, params.getNdotVUnclamped()); + scalar_type _pdf = vndf(lambda); + onePlusLambda_V = vndf.onePlusLambda_V; + + return _pdf; + } + scalar_type pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params, NBL_REF_ARG(scalar_type) onePlusLambda_V) + { + scalar_type ndf, lambda; + ndf::SAnisotropicParams ndfparams = ndf::SAnisotropicParams::create(A.x, A.y, A.x*A.x, A.y*A.y, params.getTdotH2(), params.getBdotH2(), params.getNdotH2()); + ndf::Beckmann beckmann_ndf; + ndf = beckmann_ndf(ndfparams); + + smith::Beckmann beckmann_smith; + const scalar_type c2 = beckmann_smith.C2(params.getTdotV2(), params.getBdotV2(), params.getNdotV2(), A.x, A.y); + lambda = beckmann_smith.Lambda(c2); + + smith::brdf::VNDF_pdf > vndf = smith::brdf::VNDF_pdf >::create(ndf, params.getNdotVUnclamped()); + scalar_type _pdf = vndf(lambda); + onePlusLambda_V = vndf.onePlusLambda_V; + + return _pdf; + } + + scalar_type pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + scalar_type dummy; + return pdf(params, dummy); + } + scalar_type pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + scalar_type dummy; + return pdf(params, dummy); + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + scalar_type onePlusLambda_V; + scalar_type _pdf = pdf(params, onePlusLambda_V); + + smith::Beckmann beckmann_smith; + spectral_type quo = (spectral_type)0.0; + if (params.getNdotLUnclamped() > numeric_limits::min && params.getNdotVUnclamped() > numeric_limits::min) + { + smith::SIsotropicParams smithparams = smith::SIsotropicParams::create(A.x*A.x, params.getNdotV2(), params.getNdotL2(), onePlusLambda_V); + scalar_type G2_over_G1 = beckmann_smith.G2_over_G1(smithparams); + fresnel::Conductor f = fresnel::Conductor::create(ior0, ior1, params.getVdotH()); + const spectral_type reflectance = f(); + quo = reflectance * G2_over_G1; + } + + return quotient_pdf_type::create(quo, _pdf); + } + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + scalar_type onePlusLambda_V; + scalar_type _pdf = pdf(params, onePlusLambda_V); + + smith::Beckmann beckmann_smith; + spectral_type quo = (spectral_type)0.0; + if (params.getNdotLUnclamped() > numeric_limits::min && params.getNdotVUnclamped() > numeric_limits::min) + { + smith::SAnisotropicParams smithparams = smith::SAnisotropicParams::create(A.x*A.x, A.y*A.y, params.getTdotV2(), params.getBdotV2(), params.getNdotV2(), params.getTdotL2(), params.getBdotL2(), params.getNdotL2(), onePlusLambda_V); + scalar_type G2_over_G1 = beckmann_smith.G2_over_G1(smithparams); + fresnel::Conductor f = fresnel::Conductor::create(ior0, ior1, params.getVdotH()); + const spectral_type reflectance = f(); + quo = reflectance * G2_over_G1; + } + + return quotient_pdf_type::create(quo, _pdf); + } + + vector2_type A; + spectral_type ior0, ior1; +}; + +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/ggx.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/ggx.hlsl new file mode 100644 index 0000000000..601ed78eb9 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/reflection/ggx.hlsl @@ -0,0 +1,340 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_BXDF_REFLECTION_GGX_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_REFLECTION_GGX_INCLUDED_ + +#include "nbl/builtin/hlsl/bxdf/common.hlsl" +#include "nbl/builtin/hlsl/sampling/cos_weighted.hlsl" +#include "nbl/builtin/hlsl/bxdf/geom_smith.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace reflection +{ + +template +struct GGXParams; + +template +NBL_PARTIAL_REQ_TOP(!surface_interactions::Anisotropic && !AnisotropicMicrofacetCache) +struct GGXParams && !AnisotropicMicrofacetCache) > +{ + using this_t = GGXParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, NBL_CONST_REF_ARG(MC) cache, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval.cache = cache; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + Scalar getNdotH() NBL_CONST_MEMBER_FUNC { return cache.getNdotH(); } + Scalar getNdotH2() NBL_CONST_MEMBER_FUNC { return cache.getNdotH2(); } + Scalar getVdotH() NBL_CONST_MEMBER_FUNC { return cache.getVdotH(); } + Scalar getLdotH() NBL_CONST_MEMBER_FUNC { return cache.getLdotH(); } + + LS _sample; + SI interaction; + MC cache; + BxDFClampMode _clamp; +}; +template +NBL_PARTIAL_REQ_TOP(surface_interactions::Anisotropic && AnisotropicMicrofacetCache) +struct GGXParams && AnisotropicMicrofacetCache) > +{ + using this_t = GGXParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, NBL_CONST_REF_ARG(MC) cache, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval.cache = cache; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + Scalar getNdotH() NBL_CONST_MEMBER_FUNC { return cache.getNdotH(); } + Scalar getNdotH2() NBL_CONST_MEMBER_FUNC { return cache.getNdotH2(); } + Scalar getVdotH() NBL_CONST_MEMBER_FUNC { return cache.getVdotH(); } + Scalar getLdotH() NBL_CONST_MEMBER_FUNC { return cache.getLdotH(); } + + // aniso + Scalar getTdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getTdotL() * _sample.getTdotL(); } + Scalar getBdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getBdotL() * _sample.getBdotL(); } + Scalar getTdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getTdotV() * interaction.getTdotV(); } + Scalar getBdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getBdotV() * interaction.getBdotV(); } + Scalar getTdotH2() NBL_CONST_MEMBER_FUNC {return cache.getTdotH() * cache.getTdotH(); } + Scalar getBdotH2() NBL_CONST_MEMBER_FUNC {return cache.getBdotH() * cache.getBdotH(); } + + LS _sample; + SI interaction; + MC cache; + BxDFClampMode _clamp; +}; + +template && surface_interactions::Isotropic && surface_interactions::Anisotropic && CreatableIsotropicMicrofacetCache && AnisotropicMicrofacetCache) +struct SGGXBxDF +{ + using this_t = SGGXBxDF; + using scalar_type = typename LS::scalar_type; + using ray_dir_info_type = typename LS::ray_dir_info_type; + using vector2_type = vector; + using vector3_type = vector; + using matrix2x3_type = matrix; + + using isotropic_interaction_type = Iso; + using anisotropic_interaction_type = Aniso; + using sample_type = LS; + using spectral_type = Spectrum; + using quotient_pdf_type = sampling::quotient_and_pdf; + using isocache_type = IsoCache; + using anisocache_type = AnisoCache; + + using params_isotropic_t = GGXParams; + using params_anisotropic_t = GGXParams; + + + // iso + static this_t create(scalar_type A, NBL_CONST_REF_ARG(spectral_type) ior0, NBL_CONST_REF_ARG(spectral_type) ior1) + { + this_t retval; + retval.A = vector2_type(A,A); + retval.ior0 = ior0; + retval.ior1 = ior1; + return retval; + } + + // aniso + static this_t create(scalar_type ax, scalar_type ay, NBL_CONST_REF_ARG(spectral_type) ior0, NBL_CONST_REF_ARG(spectral_type) ior1) + { + this_t retval; + retval.A = vector2_type(ax,ay); + retval.ior0 = ior0; + retval.ior1 = ior1; + return retval; + } + + static this_t create(NBL_CONST_REF_ARG(SBxDFCreationParams) params) + { + if (params.is_aniso) + return create(params.A.x, params.A.y, params.ior0, params.ior1); + else + return create(params.A.x, params.ior0, params.ior1); + } + + void init(NBL_CONST_REF_ARG(SBxDFCreationParams) params) + { + A = params.A; + ior0 = params.ior0; + ior1 = params.ior1; + } + + scalar_type __eval_DG_wo_clamps(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + scalar_type a2 = A.x*A.x; + ndf::SIsotropicParams ndfparams = ndf::SIsotropicParams::create(a2, params.getNdotH(), params.getNdotH2()); + ndf::GGX ggx_ndf; + scalar_type NG = ggx_ndf(ndfparams); + if (a2 > numeric_limits::min) + { + smith::SIsotropicParams smithparams = smith::SIsotropicParams::create(a2, params.getNdotV(), params.getNdotV2(), params.getNdotL(), params.getNdotL2()); + smith::GGX ggx_smith; + NG *= ggx_smith.correlated_wo_numerator(smithparams); + } + return NG; + } + scalar_type __eval_DG_wo_clamps(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + const scalar_type ax2 = A.x*A.x; + const scalar_type ay2 = A.y*A.y; + ndf::SAnisotropicParams ndfparams = ndf::SAnisotropicParams::create(A.x, A.y, ax2, ay2, params.getTdotH2(), params.getBdotH2(), params.getNdotH2()); + ndf::GGX ggx_ndf; + scalar_type NG = ggx_ndf(ndfparams); + if (any >(A > (vector2_type)numeric_limits::min)) + { + smith::SAnisotropicParams smithparams = smith::SAnisotropicParams::create(ax2, ay2, params.getNdotV(), params.getTdotV2(), params.getBdotV2(), params.getNdotV2(), params.getNdotL(), params.getTdotL2(), params.getBdotL2(), params.getNdotL2()); + smith::GGX ggx_smith; + NG *= ggx_smith.correlated_wo_numerator(smithparams); + } + return NG; + } + + spectral_type eval(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + if (params.getNdotLUnclamped() > numeric_limits::min && params.getNdotVUnclamped() > numeric_limits::min) + { + scalar_type scalar_part = __eval_DG_wo_clamps(params); + ndf::microfacet_to_light_measure_transform,ndf::REFLECT_BIT> microfacet_transform = ndf::microfacet_to_light_measure_transform,ndf::REFLECT_BIT>::create(scalar_part, params.getNdotL()); + fresnel::Conductor f = fresnel::Conductor::create(ior0, ior1, params.getVdotH()); + return f() * microfacet_transform(); + } + else + return (spectral_type)0.0; + } + spectral_type eval(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + if (params.getNdotLUnclamped() > numeric_limits::min && params.getNdotVUnclamped() > numeric_limits::min) + { + scalar_type scalar_part = __eval_DG_wo_clamps(params); + ndf::microfacet_to_light_measure_transform,ndf::REFLECT_BIT> microfacet_transform = ndf::microfacet_to_light_measure_transform,ndf::REFLECT_BIT>::create(scalar_part, params.getNdotL()); + fresnel::Conductor f = fresnel::Conductor::create(ior0, ior1, params.getVdotH()); + return f() * microfacet_transform(); + } + else + return (spectral_type)0.0; + } + + vector3_type __generate(NBL_CONST_REF_ARG(vector3_type) localV, NBL_CONST_REF_ARG(vector2_type) u) + { + vector3_type V = nbl::hlsl::normalize(vector3_type(A.x*localV.x, A.y*localV.y, localV.z));//stretch view vector so that we're sampling as if roughness=1.0 + + scalar_type lensq = V.x*V.x + V.y*V.y; + vector3_type T1 = lensq > 0.0 ? vector3_type(-V.y, V.x, 0.0) * rsqrt(lensq) : vector3_type(1.0,0.0,0.0); + vector3_type T2 = cross(V,T1); + + scalar_type r = sqrt(u.x); + scalar_type phi = 2.0 * numbers::pi * u.y; + scalar_type t1 = r * cos(phi); + scalar_type t2 = r * sin(phi); + scalar_type s = 0.5 * (1.0 + V.z); + t2 = (1.0 - s)*sqrt(1.0 - t1*t1) + s*t2; + + //reprojection onto hemisphere + //TODO try it wothout the max(), not sure if -t1*t1-t2*t2>-1.0 + vector3_type H = t1*T1 + t2*T2 + sqrt(max(0.0, 1.0-t1*t1-t2*t2))*V; + //unstretch + return nbl::hlsl::normalize(vector3_type(A.x*H.x, A.y*H.y, H.z)); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(vector2_type) u, NBL_REF_ARG(anisocache_type) cache) + { + const vector3_type localV = interaction.getTangentSpaceV(); + const vector3_type H = __generate(localV, u); + + cache = anisocache_type::create(localV, H); + ray_dir_info_type localL; + bxdf::Reflect r = bxdf::Reflect::create(localV, H, cache.iso_cache.getVdotH()); + localL.direction = r(); + + return sample_type::createFromTangentSpace(localV, localL, interaction.getFromTangentSpace()); + } + + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(vector2_type) u, NBL_REF_ARG(isocache_type) cache) + { + anisocache_type anisocache; + sample_type s = generate(anisotropic_interaction_type::create(interaction), u, anisocache); + cache = anisocache.iso_cache; + return s; + } + + scalar_type pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + scalar_type ndf, G1_over_2NdotV; + const scalar_type a2 = A.x*A.x; + ndf::SIsotropicParams ndfparams = ndf::SIsotropicParams::create(a2, params.getNdotH(), params.getNdotH2()); + ndf::GGX ggx_ndf; + ndf = ggx_ndf(ndfparams); + + smith::GGX ggx_smith; + const scalar_type devsh_v = ggx_smith.devsh_part(params.getNdotV2(), a2, 1.0-a2); + G1_over_2NdotV = ggx_smith.G1_wo_numerator(params.getNdotVUnclamped(), devsh_v); + + smith::brdf::VNDF_pdf > vndf = smith::brdf::VNDF_pdf >::create(ndf, params.getNdotVUnclamped()); + return vndf(G1_over_2NdotV); + } + scalar_type pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + scalar_type ndf, G1_over_2NdotV; + const scalar_type ax2 = A.x*A.x; + const scalar_type ay2 = A.y*A.y; + ndf::SAnisotropicParams ndfparams = ndf::SAnisotropicParams::create(A.x, A.y, ax2, ay2, params.getTdotH2(), params.getBdotH2(), params.getNdotH2()); + ndf::GGX ggx_ndf; + ndf = ggx_ndf(ndfparams); + + smith::GGX ggx_smith; + const scalar_type devsh_v = ggx_smith.devsh_part(params.getTdotV2(), params.getBdotV2(), params.getNdotV2(), ax2, ay2); + G1_over_2NdotV = ggx_smith.G1_wo_numerator(params.getNdotVUnclamped(), devsh_v); + + smith::brdf::VNDF_pdf > vndf = smith::brdf::VNDF_pdf >::create(ndf, params.getNdotVUnclamped()); + return vndf(G1_over_2NdotV); + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + scalar_type _pdf = pdf(params); + + spectral_type quo = (spectral_type)0.0; + if (params.getNdotLUnclamped() > numeric_limits::min && params.getNdotVUnclamped() > numeric_limits::min) + { + scalar_type G2_over_G1; + smith::GGX ggx_smith; + + const scalar_type a2 = A.x*A.x; + smith::SIsotropicParams smithparams = smith::SIsotropicParams::create(a2, params.getNdotVUnclamped(), params.getNdotV2(), params.getNdotLUnclamped(), params.getNdotL2()); + G2_over_G1 = ggx_smith.G2_over_G1(smithparams); + + fresnel::Conductor f = fresnel::Conductor::create(ior0, ior1, params.getVdotH()); + const spectral_type reflectance = f(); + quo = reflectance * G2_over_G1; + } + + return quotient_pdf_type::create(quo, _pdf); + } + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + scalar_type _pdf = pdf(params); + + spectral_type quo = (spectral_type)0.0; + if (params.getNdotLUnclamped() > numeric_limits::min && params.getNdotVUnclamped() > numeric_limits::min) + { + scalar_type G2_over_G1; + smith::GGX ggx_smith; + + const scalar_type ax2 = A.x*A.x; + const scalar_type ay2 = A.y*A.y; + smith::SAnisotropicParams smithparams = smith::SAnisotropicParams::create(ax2, ay2, params.getNdotVUnclamped(), params.getTdotV2(), params.getBdotV2(), params.getNdotV2(), params.getNdotLUnclamped(), params.getTdotL2(), params.getBdotL2(), params.getNdotL2()); + G2_over_G1 = ggx_smith.G2_over_G1(smithparams); + + fresnel::Conductor f = fresnel::Conductor::create(ior0, ior1, params.getVdotH()); + const spectral_type reflectance = f(); + quo = reflectance * G2_over_G1; + } + + return quotient_pdf_type::create(quo, _pdf); + } + + vector2_type A; + spectral_type ior0, ior1; +}; + +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/lambertian.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/lambertian.hlsl new file mode 100644 index 0000000000..394a7e677f --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/reflection/lambertian.hlsl @@ -0,0 +1,176 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_BXDF_REFLECTION_LAMBERTIAN_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_REFLECTION_LAMBERTIAN_INCLUDED_ + +#include "nbl/builtin/hlsl/bxdf/common.hlsl" +#include "nbl/builtin/hlsl/sampling/cos_weighted.hlsl" +#include "nbl/builtin/hlsl/bxdf/geom_smith.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace reflection +{ + +template +struct LambertianParams; + +template +NBL_PARTIAL_REQ_TOP(!surface_interactions::Anisotropic) +struct LambertianParams) > +{ + using this_t = LambertianParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + + LS _sample; + SI interaction; + BxDFClampMode _clamp; +}; +template +NBL_PARTIAL_REQ_TOP(surface_interactions::Anisotropic) +struct LambertianParams) > +{ + using this_t = LambertianParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + + // aniso + Scalar getTdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getTdotL() * _sample.getTdotL(); } + Scalar getBdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getBdotL() * _sample.getBdotL(); } + Scalar getTdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getTdotV() * interaction.getTdotV(); } + Scalar getBdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getBdotV() * interaction.getBdotV(); } + + LS _sample; + SI interaction; + BxDFClampMode _clamp; +}; + +template && surface_interactions::Isotropic && surface_interactions::Anisotropic) +struct SLambertianBxDF +{ + using this_t = SLambertianBxDF; + using scalar_type = typename LS::scalar_type; + using ray_dir_info_type = typename LS::ray_dir_info_type; + using isotropic_interaction_type = Iso; + using anisotropic_interaction_type = Aniso; + using sample_type = LS; + using spectral_type = Spectrum; + using quotient_pdf_type = sampling::quotient_and_pdf; + + using params_isotropic_t = LambertianParams; + using params_anisotropic_t = LambertianParams; + + + static this_t create() + { + this_t retval; + // nothing here, just keeping in convention with others + return retval; + } + + static this_t create(NBL_CONST_REF_ARG(SBxDFCreationParams) params) + { + return create(); + } + + void init(NBL_CONST_REF_ARG(SBxDFCreationParams) params) + { + // do nothing + } + + scalar_type __eval_pi_factored_out(scalar_type maxNdotL) + { + return maxNdotL; + } + + scalar_type eval(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + return __eval_pi_factored_out(params.getNdotL()) * numbers::inv_pi; + } + scalar_type eval(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + return __eval_pi_factored_out(params.getNdotL()) * numbers::inv_pi; + } + + sample_type generate_wo_clamps(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(vector) u) + { + ray_dir_info_type L; + L.direction = sampling::ProjectedHemisphere::generate(u); + return sample_type::createFromTangentSpace(interaction.getTangentSpaceV(), L, interaction.getFromTangentSpace()); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(vector) u) + { + return generate_wo_clamps(interaction, u); + } + + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(vector) u) + { + return generate_wo_clamps(anisotropic_interaction_type::create(interaction), u); + } + + scalar_type pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + return sampling::ProjectedHemisphere::pdf(params.getNdotL()); + } + scalar_type pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + return sampling::ProjectedHemisphere::pdf(params.getNdotL()); + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + sampling::quotient_and_pdf qp = sampling::ProjectedHemisphere::template quotient_and_pdf(params.getNdotL()); + return quotient_pdf_type::create(hlsl::promote(qp.quotient), qp.pdf); + } + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + sampling::quotient_and_pdf qp = sampling::ProjectedHemisphere::template quotient_and_pdf(params.getNdotL()); + return quotient_pdf_type::create(hlsl::promote(qp.quotient), qp.pdf); + } +}; + +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/oren_nayar.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/oren_nayar.hlsl new file mode 100644 index 0000000000..281f3f12e1 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/reflection/oren_nayar.hlsl @@ -0,0 +1,187 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_BXDF_REFLECTION_OREN_NAYAR_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_REFLECTION_OREN_NAYAR_INCLUDED_ + +#include "nbl/builtin/hlsl/bxdf/common.hlsl" +#include "nbl/builtin/hlsl/sampling/cos_weighted.hlsl" +#include "nbl/builtin/hlsl/bxdf/geom_smith.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace reflection +{ + +template +struct OrenNayarParams; + +template +NBL_PARTIAL_REQ_TOP(!surface_interactions::Anisotropic) +struct OrenNayarParams) > +{ + using this_t = OrenNayarParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + + LS _sample; + SI interaction; + BxDFClampMode _clamp; +}; +template +NBL_PARTIAL_REQ_TOP(surface_interactions::Anisotropic) +struct OrenNayarParams) > +{ + using this_t = OrenNayarParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + + // aniso + Scalar getTdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getTdotL() * _sample.getTdotL(); } + Scalar getBdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getBdotL() * _sample.getBdotL(); } + Scalar getTdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getTdotV() * interaction.getTdotV(); } + Scalar getBdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getBdotV() * interaction.getBdotV(); } + + LS _sample; + SI interaction; + BxDFClampMode _clamp; +}; + +template && surface_interactions::Isotropic && surface_interactions::Anisotropic) +struct SOrenNayarBxDF +{ + using this_t = SOrenNayarBxDF; + using scalar_type = typename LS::scalar_type; + using vector2_type = vector; + using ray_dir_info_type = typename LS::ray_dir_info_type; + + using isotropic_interaction_type = Iso; + using anisotropic_interaction_type = Aniso; + using sample_type = LS; + using spectral_type = Spectrum; + using quotient_pdf_type = sampling::quotient_and_pdf; + + using params_isotropic_t = OrenNayarParams; + using params_anisotropic_t = OrenNayarParams; + + + static this_t create(scalar_type A) + { + this_t retval; + retval.A = A; + return retval; + } + + static this_t create(NBL_CONST_REF_ARG(SBxDFCreationParams) params) + { + return create(params.A.x); + } + + void init(NBL_CONST_REF_ARG(SBxDFCreationParams) params) + { + A = params.A.x; + } + + scalar_type __rec_pi_factored_out_wo_clamps(scalar_type VdotL, scalar_type maxNdotL, scalar_type maxNdotV) + { + scalar_type A2 = A * 0.5; + vector2_type AB = vector2_type(1.0, 0.0) + vector2_type(-0.5, 0.45) * vector2_type(A2, A2) / vector2_type(A2 + 0.33, A2 + 0.09); + scalar_type C = 1.0 / max(maxNdotL, maxNdotV); + + scalar_type cos_phi_sin_theta = max(VdotL - maxNdotL * maxNdotV, 0.0); + return (AB.x + AB.y * cos_phi_sin_theta * C); + } + + scalar_type eval(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + return params.getNdotL() * numbers::inv_pi * __rec_pi_factored_out_wo_clamps(params.getVdotL(), params.getNdotL(), params.getNdotV()); + } + scalar_type eval(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + return params.getNdotL() * numbers::inv_pi * __rec_pi_factored_out_wo_clamps(params.getVdotL(), params.getNdotL(), params.getNdotV()); + } + + sample_type generate_wo_clamps(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(vector2_type) u) + { + ray_dir_info_type L; + L.direction = sampling::ProjectedHemisphere::generate(u); + return sample_type::createFromTangentSpace(interaction.getTangentSpaceV(), L, interaction.getFromTangentSpace()); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(vector2_type) u) + { + return generate_wo_clamps(interaction, u); + } + + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(vector) u) + { + return generate_wo_clamps(anisotropic_interaction_type::create(interaction), u); + } + + scalar_type pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + return sampling::ProjectedHemisphere::pdf(params.getNdotL()); + } + scalar_type pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + return sampling::ProjectedHemisphere::pdf(params.getNdotL()); + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + scalar_type _pdf = pdf(params); + scalar_type q = __rec_pi_factored_out_wo_clamps(params.getVdotL(), params.getNdotL(), params.getNdotV()); + return quotient_pdf_type::create(hlsl::promote(q), _pdf); + } + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + scalar_type _pdf = pdf(params); + scalar_type q = __rec_pi_factored_out_wo_clamps(params.getVdotL(), params.getNdotL(), params.getNdotV()); + return quotient_pdf_type::create(hlsl::promote(q), _pdf); + } + + scalar_type A; +}; + +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/transmission.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission.hlsl index ff708548b8..81f531e1a6 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission.hlsl @@ -1,45 +1,22 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h +// For conditions of distribution and use, see copyright notice nabla.h #ifndef _NBL_BUILTIN_HLSL_BXDF_TRANSMISSION_INCLUDED_ #define _NBL_BUILTIN_HLSL_BXDF_TRANSMISSION_INCLUDED_ -#include +#include "nbl/builtin/hlsl/bxdf/transmission/lambertian.hlsl" +#include "nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl" +#include "nbl/builtin/hlsl/bxdf/transmission/beckmann.hlsl" +#include "nbl/builtin/hlsl/bxdf/transmission/ggx.hlsl" namespace nbl { namespace hlsl { -namespace bxdf -{ -namespace transmission -{ -template -LightSample cos_generate(const surface_interactions::Isotropic interaction) -{ - return LightSample(interaction.V.transmit(),-1.f,interaction.N); -} -template -LightSample cos_generate(const surface_interactions::Anisotropic interaction) -{ - return LightSample(interaction.V.transmit(),-1.f,interaction.T,interaction.B,interaction.N); -} +// After Clang-HLSL introduces https://en.cppreference.com/w/cpp/language/namespace_alias +// namespace bsdf = bxdf::transmission; -// Why don't we check that the incoming and outgoing directions equal each other -// (or similar for other delta distributions such as reflect, or smooth [thin] dielectrics): -// - The `quotient_and_pdf` functions are meant to be used with MIS and RIS -// - Our own generator can never pick an improbable path, so no checking necessary -// - For other generators the estimator will be `f_BSDF*f_Light*f_Visibility*clampedCos(theta)/(1+(p_BSDF^alpha+p_otherNonChosenGenerator^alpha+...)/p_ChosenGenerator^alpha)` -// therefore when `p_BSDF` equals `nbl_glsl_FLT_INF` it will drive the overall MIS estimator for the other generators to 0 so no checking necessary -template -quotient_and_pdf cos_quotient_and_pdf() -{ - return quotient_and_pdf::create(SpectralBins(1.f),nbl::hlsl::numeric_limits::inf()); -} - -} -} } } diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/beckmann.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/beckmann.hlsl new file mode 100644 index 0000000000..652e69e459 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/transmission/beckmann.hlsl @@ -0,0 +1,339 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_BXDF_TRANSMISSION_BECKMANN_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_TRANSMISSION_BECKMANN_INCLUDED_ + +#include "nbl/builtin/hlsl/bxdf/common.hlsl" +#include "nbl/builtin/hlsl/sampling/cos_weighted.hlsl" +#include "nbl/builtin/hlsl/bxdf/reflection.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace transmission +{ + +template +struct BeckmannParams; + +template +NBL_PARTIAL_REQ_TOP(!surface_interactions::Anisotropic && !AnisotropicMicrofacetCache) +struct BeckmannParams && !AnisotropicMicrofacetCache) > +{ + using this_t = BeckmannParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, NBL_CONST_REF_ARG(MC) cache, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval.cache = cache; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + Scalar getNdotH() NBL_CONST_MEMBER_FUNC { return cache.getNdotH(); } + Scalar getNdotH2() NBL_CONST_MEMBER_FUNC { return cache.getNdotH2(); } + Scalar getVdotH() NBL_CONST_MEMBER_FUNC { return cache.getVdotH(); } + Scalar getLdotH() NBL_CONST_MEMBER_FUNC { return cache.getLdotH(); } + + LS _sample; + SI interaction; + MC cache; + BxDFClampMode _clamp; +}; +template +NBL_PARTIAL_REQ_TOP(surface_interactions::Anisotropic && AnisotropicMicrofacetCache) +struct BeckmannParams && AnisotropicMicrofacetCache) > +{ + using this_t = BeckmannParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, NBL_CONST_REF_ARG(MC) cache, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval.cache = cache; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + Scalar getNdotH() NBL_CONST_MEMBER_FUNC { return cache.getNdotH(); } + Scalar getNdotH2() NBL_CONST_MEMBER_FUNC { return cache.getNdotH2(); } + Scalar getVdotH() NBL_CONST_MEMBER_FUNC { return cache.getVdotH(); } + Scalar getLdotH() NBL_CONST_MEMBER_FUNC { return cache.getLdotH(); } + + // aniso + Scalar getTdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getTdotL() * _sample.getTdotL(); } + Scalar getBdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getBdotL() * _sample.getBdotL(); } + Scalar getTdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getTdotV() * interaction.getTdotV(); } + Scalar getBdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getBdotV() * interaction.getBdotV(); } + Scalar getTdotH2() NBL_CONST_MEMBER_FUNC {return cache.getTdotH() * cache.getTdotH(); } + Scalar getBdotH2() NBL_CONST_MEMBER_FUNC {return cache.getBdotH() * cache.getBdotH(); } + + LS _sample; + SI interaction; + MC cache; + BxDFClampMode _clamp; +}; + +template && surface_interactions::Isotropic && surface_interactions::Anisotropic && CreatableIsotropicMicrofacetCache && AnisotropicMicrofacetCache) +struct SBeckmannDielectricBxDF +{ + using this_t = SBeckmannDielectricBxDF; + using scalar_type = typename LS::scalar_type; + using ray_dir_info_type = typename LS::ray_dir_info_type; + using vector2_type = vector; + using vector3_type = vector; + using matrix3x3_type = matrix; + + using isotropic_interaction_type = Iso; + using anisotropic_interaction_type = Aniso; + using sample_type = LS; + using spectral_type = Spectrum; + using quotient_pdf_type = sampling::quotient_and_pdf; + using isocache_type = IsoCache; + using anisocache_type = AnisoCache; + using brdf_type = reflection::SBeckmannBxDF; + + using params_isotropic_t = BeckmannParams; + using params_anisotropic_t = BeckmannParams; + + + static this_t create(scalar_type eta, scalar_type A) + { + this_t retval; + retval.eta = eta; + retval.A = vector2_type(A, A); + return retval; + } + + static this_t create(scalar_type eta, scalar_type ax, scalar_type ay) + { + this_t retval; + retval.eta = eta; + retval.A = vector2_type(ax, ay); + return retval; + } + + static this_t create(NBL_CONST_REF_ARG(SBxDFCreationParams) params) + { + if (params.is_aniso) + return create(params.eta, params.A.x, params.A.y); + else + return create(params.eta, params.A.x); + } + + void init(NBL_CONST_REF_ARG(SBxDFCreationParams) params) + { + A = params.A; + eta = params.eta; + } + + spectral_type eval(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(params.getVdotH(), eta); + const scalar_type orientedEta2 = orientedEta.value * orientedEta.value; + + const scalar_type VdotHLdotH = params.getVdotH() * params.getLdotH(); + const bool transmitted = VdotHLdotH < 0.0; + + spectral_type dummyior; + brdf_type beckmann = brdf_type::create(A.x, dummyior, dummyior); + typename brdf_type::params_isotropic_t brdf_params = typename brdf_type::params_isotropic_t::create(params._sample, params.interaction, params.cache, params._clamp); + const scalar_type scalar_part = beckmann.__eval_DG_wo_clamps(brdf_params); + + ndf::microfacet_to_light_measure_transform,ndf::REFLECT_REFRACT_BIT> microfacet_transform = + ndf::microfacet_to_light_measure_transform,ndf::REFLECT_REFRACT_BIT>::create(scalar_part,params.getNdotV(),transmitted,params.getVdotH(),params.getLdotH(),VdotHLdotH,orientedEta.value); + scalar_type f = fresnel::Dielectric::__call(orientedEta2, nbl::hlsl::abs(params.getVdotH())); + return hlsl::promote(f) * microfacet_transform(); + } + spectral_type eval(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(params.getVdotH(), eta); + const scalar_type orientedEta2 = orientedEta.value * orientedEta.value; + + const scalar_type VdotHLdotH = params.getVdotH() * params.getLdotH(); + const bool transmitted = VdotHLdotH < 0.0; + + spectral_type dummyior; + brdf_type beckmann = brdf_type::create(A.x, A.y, dummyior, dummyior); + typename brdf_type::params_anisotropic_t brdf_params = typename brdf_type::params_anisotropic_t::create(params._sample, params.interaction, params.cache, params._clamp); + const scalar_type scalar_part = beckmann.__eval_DG_wo_clamps(brdf_params); + + ndf::microfacet_to_light_measure_transform,ndf::REFLECT_REFRACT_BIT> microfacet_transform = + ndf::microfacet_to_light_measure_transform,ndf::REFLECT_REFRACT_BIT>::create(scalar_part,params.getNdotV(),transmitted,params.getVdotH(),params.getLdotH(),VdotHLdotH,orientedEta.value); + scalar_type f = fresnel::Dielectric::__call(orientedEta2, nbl::hlsl::abs(params.getVdotH())); + return hlsl::promote(f) * microfacet_transform(); + } + + sample_type __generate_wo_clamps(NBL_CONST_REF_ARG(vector3_type) localV, NBL_CONST_REF_ARG(vector3_type) H, NBL_CONST_REF_ARG(matrix3x3_type) m, NBL_REF_ARG(vector3_type) u, NBL_CONST_REF_ARG(fresnel::OrientedEtas) orientedEta, NBL_CONST_REF_ARG(fresnel::OrientedEtaRcps) rcpEta, NBL_REF_ARG(anisocache_type) cache) + { + const scalar_type localVdotH = nbl::hlsl::dot(localV,H); + const scalar_type reflectance = fresnel::Dielectric::__call(orientedEta.value * orientedEta.value,nbl::hlsl::abs(localVdotH)); + + scalar_type rcpChoiceProb; + bool transmitted = math::partitionRandVariable(reflectance, u.z, rcpChoiceProb); + + cache = anisocache_type::create(localV, H); + + const scalar_type VdotH = cache.iso_cache.getVdotH(); + Refract r; + r.recomputeNdotT(VdotH < 0.0, VdotH * VdotH, rcpEta.value2); + cache.iso_cache.LdotH = hlsl::mix(VdotH, r.NdotT, transmitted); + ray_dir_info_type localL; + bxdf::ReflectRefract rr = bxdf::ReflectRefract::create(transmitted, localV, H, VdotH, cache.iso_cache.getLdotH(), rcpEta.value); + localL.direction = rr(transmitted); + + return sample_type::createFromTangentSpace(localV, localL, m); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u, NBL_REF_ARG(anisocache_type) cache) + { + const vector3_type localV = interaction.getTangentSpaceV(); + + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(interaction.isotropic.getNdotV(), eta); + fresnel::OrientedEtaRcps rcpEta = fresnel::OrientedEtaRcps::create(interaction.isotropic.getNdotV(), eta); + + const vector3_type upperHemisphereV = orientedEta.backside ? -localV : localV; + + spectral_type dummyior; + brdf_type beckmann = brdf_type::create(A.x, A.y, dummyior, dummyior); + const vector3_type H = beckmann.__generate(upperHemisphereV, u.xy); + + return __generate_wo_clamps(localV, H, interaction.getFromTangentSpace(), u, orientedEta, rcpEta, cache); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u) + { + anisocache_type dummycache; + return generate(interaction, u, dummycache); + } + + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u, NBL_REF_ARG(isocache_type) cache) + { + anisocache_type anisocache; + sample_type s = generate(anisotropic_interaction_type::create(interaction), u, anisocache); + cache = anisocache.iso_cache; + return s; + } + + scalar_type pdf(NBL_CONST_REF_ARG(params_isotropic_t) params, NBL_REF_ARG(scalar_type) onePlusLambda_V) + { + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(params.getVdotH(), eta); + const scalar_type orientedEta2 = orientedEta.value * orientedEta.value; + + const scalar_type VdotHLdotH = params.getVdotH() * params.getLdotH(); + const bool transmitted = VdotHLdotH < 0.0; + + const scalar_type reflectance = fresnel::Dielectric::__call(orientedEta2, nbl::hlsl::abs(params.getVdotH())); + + scalar_type ndf, lambda; + const scalar_type a2 = A.x*A.x; + ndf::SIsotropicParams ndfparams = ndf::SIsotropicParams::create(a2, params.getNdotH(), params.getNdotH2()); + ndf::Beckmann beckmann_ndf; + ndf = beckmann_ndf(ndfparams); + + smith::Beckmann beckmann_smith; + lambda = beckmann_smith.Lambda(params.getNdotV2(), a2); + + smith::bsdf::VNDF_pdf > vndf = smith::bsdf::VNDF_pdf >::create(ndf, params.getNdotV()); + scalar_type _pdf = vndf(lambda,transmitted,params.getVdotH(),params.getLdotH(),VdotHLdotH,orientedEta.value,reflectance); + onePlusLambda_V = vndf.onePlusLambda_V; + + return _pdf; + } + scalar_type pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params, NBL_REF_ARG(scalar_type) onePlusLambda_V) + { + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(params.getVdotH(), eta); + const scalar_type orientedEta2 = orientedEta.value * orientedEta.value; + + const scalar_type VdotHLdotH = params.getVdotH() * params.getLdotH(); + const bool transmitted = VdotHLdotH < 0.0; + + const scalar_type reflectance = fresnel::Dielectric::__call(orientedEta2, nbl::hlsl::abs(params.getVdotH())); + + scalar_type ndf, lambda; + const scalar_type ax2 = A.x*A.x; + const scalar_type ay2 = A.y*A.y; + ndf::SAnisotropicParams ndfparams = ndf::SAnisotropicParams::create(A.x, A.y, ax2, ay2, params.getTdotH2(), params.getBdotH2(), params.getNdotH2()); + ndf::Beckmann beckmann_ndf; + ndf = beckmann_ndf(ndfparams); + + smith::Beckmann beckmann_smith; + scalar_type c2 = beckmann_smith.C2(params.getTdotV2(), params.getBdotV2(), params.getNdotV2(), ax2, ay2); + lambda = beckmann_smith.Lambda(c2); + + smith::bsdf::VNDF_pdf > vndf = smith::bsdf::VNDF_pdf >::create(ndf, params.getNdotV()); + scalar_type _pdf = vndf(lambda,transmitted,params.getVdotH(),params.getLdotH(),VdotHLdotH,orientedEta.value,reflectance); + onePlusLambda_V = vndf.onePlusLambda_V; + + return _pdf; + } + + scalar_type pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + scalar_type dummy; + return pdf(params, dummy); + } + scalar_type pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + scalar_type dummy; + return pdf(params, dummy); + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + scalar_type onePlusLambda_V; + scalar_type _pdf = pdf(params, onePlusLambda_V); + + scalar_type quo; + smith::SIsotropicParams smithparams = smith::SIsotropicParams::create(A.x*A.x, params.getNdotV2(), params.getNdotL2(), onePlusLambda_V); + smith::Beckmann beckmann_smith; + quo = beckmann_smith.G2_over_G1(smithparams); + + return quotient_pdf_type::create((spectral_type)(quo), _pdf); + } + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + scalar_type onePlusLambda_V; + scalar_type _pdf = pdf(params, onePlusLambda_V); + + scalar_type quo; + smith::SAnisotropicParams smithparams = smith::SAnisotropicParams::create(A.x*A.x, A.y*A.y, params.getTdotV2(), params.getBdotV2(), params.getNdotV2(), params.getTdotL2(), params.getBdotL2(), params.getNdotL2(), onePlusLambda_V); + smith::Beckmann beckmann_smith; + quo = beckmann_smith.G2_over_G1(smithparams); + + return quotient_pdf_type::create((spectral_type)(quo), _pdf); + } + + vector2_type A; + scalar_type eta; +}; + +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/ggx.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/ggx.hlsl new file mode 100644 index 0000000000..7935b5a431 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/transmission/ggx.hlsl @@ -0,0 +1,331 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_BXDF_TRANSMISSION_GGX_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_TRANSMISSION_GGX_INCLUDED_ + +#include "nbl/builtin/hlsl/bxdf/common.hlsl" +#include "nbl/builtin/hlsl/sampling/cos_weighted.hlsl" +#include "nbl/builtin/hlsl/bxdf/reflection.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace transmission +{ + +template +struct GGXParams; + +template +NBL_PARTIAL_REQ_TOP(!surface_interactions::Anisotropic && !AnisotropicMicrofacetCache) +struct GGXParams && !AnisotropicMicrofacetCache) > +{ + using this_t = GGXParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, NBL_CONST_REF_ARG(MC) cache, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval.cache = cache; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + Scalar getNdotH() NBL_CONST_MEMBER_FUNC { return cache.getNdotH(); } + Scalar getNdotH2() NBL_CONST_MEMBER_FUNC { return cache.getNdotH2(); } + Scalar getVdotH() NBL_CONST_MEMBER_FUNC { return cache.getVdotH(); } + Scalar getLdotH() NBL_CONST_MEMBER_FUNC { return cache.getLdotH(); } + + LS _sample; + SI interaction; + MC cache; + BxDFClampMode _clamp; +}; +template +NBL_PARTIAL_REQ_TOP(surface_interactions::Anisotropic && AnisotropicMicrofacetCache) +struct GGXParams && AnisotropicMicrofacetCache) > +{ + using this_t = GGXParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, NBL_CONST_REF_ARG(MC) cache, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval.cache = cache; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + Scalar getNdotH() NBL_CONST_MEMBER_FUNC { return cache.getNdotH(); } + Scalar getNdotH2() NBL_CONST_MEMBER_FUNC { return cache.getNdotH2(); } + Scalar getVdotH() NBL_CONST_MEMBER_FUNC { return cache.getVdotH(); } + Scalar getLdotH() NBL_CONST_MEMBER_FUNC { return cache.getLdotH(); } + + // aniso + Scalar getTdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getTdotL() * _sample.getTdotL(); } + Scalar getBdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getBdotL() * _sample.getBdotL(); } + Scalar getTdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getTdotV() * interaction.getTdotV(); } + Scalar getBdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getBdotV() * interaction.getBdotV(); } + Scalar getTdotH2() NBL_CONST_MEMBER_FUNC {return cache.getTdotH() * cache.getTdotH(); } + Scalar getBdotH2() NBL_CONST_MEMBER_FUNC {return cache.getBdotH() * cache.getBdotH(); } + + LS _sample; + SI interaction; + MC cache; + BxDFClampMode _clamp; +}; + +template && surface_interactions::Isotropic && surface_interactions::Anisotropic && CreatableIsotropicMicrofacetCache && AnisotropicMicrofacetCache) +struct SGGXDielectricBxDF +{ + using this_t = SGGXDielectricBxDF; + using scalar_type = typename LS::scalar_type; + using ray_dir_info_type = typename LS::ray_dir_info_type; + using vector2_type = vector; + using vector3_type = vector; + using matrix3x3_type = matrix; + + using isotropic_interaction_type = Iso; + using anisotropic_interaction_type = Aniso; + using sample_type = LS; + using spectral_type = Spectrum; + using quotient_pdf_type = sampling::quotient_and_pdf; + using isocache_type = IsoCache; + using anisocache_type = AnisoCache; + using brdf_type = reflection::SGGXBxDF; + + using params_isotropic_t = GGXParams; + using params_anisotropic_t = GGXParams; + + static this_t create(scalar_type eta, scalar_type A) + { + this_t retval; + retval.eta = eta; + retval.A = vector2_type(A, A); + return retval; + } + + static this_t create(scalar_type eta, scalar_type ax, scalar_type ay) + { + this_t retval; + retval.eta = eta; + retval.A = vector2_type(ax, ay); + return retval; + } + + static this_t create(NBL_CONST_REF_ARG(SBxDFCreationParams) params) + { + if (params.is_aniso) + return create(params.eta, params.A.x, params.A.y); + else + return create(params.eta, params.A.x); + } + + void init(NBL_CONST_REF_ARG(SBxDFCreationParams) params) + { + A = params.A; + eta = params.eta; + } + + spectral_type eval(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(params.getVdotH(), eta); + const scalar_type orientedEta2 = orientedEta.value * orientedEta.value; + + + const scalar_type VdotHLdotH = params.getVdotH() * params.getLdotH(); + const bool transmitted = VdotHLdotH < 0.0; + + scalar_type NG_already_in_reflective_dL_measure; + spectral_type dummyior; + brdf_type ggx = brdf_type::create(A.x, dummyior, dummyior); + typename brdf_type::params_isotropic_t brdf_params = typename brdf_type::params_isotropic_t::create(params._sample, params.interaction, params.cache, params._clamp); + NG_already_in_reflective_dL_measure = ggx.__eval_DG_wo_clamps(brdf_params); + + ndf::microfacet_to_light_measure_transform,ndf::REFLECT_REFRACT_BIT> microfacet_transform = + ndf::microfacet_to_light_measure_transform,ndf::REFLECT_REFRACT_BIT>::create(NG_already_in_reflective_dL_measure,params.getNdotL(),transmitted,params.getVdotH(),params.getLdotH(),VdotHLdotH,orientedEta.value); + scalar_type f = fresnel::Dielectric::__call(orientedEta2, nbl::hlsl::abs(params.getVdotH())); + return hlsl::promote(f) * microfacet_transform(); + } + spectral_type eval(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(params.getVdotH(), eta); + const scalar_type orientedEta2 = orientedEta.value * orientedEta.value; + + + const scalar_type VdotHLdotH = params.getVdotH() * params.getLdotH(); + const bool transmitted = VdotHLdotH < 0.0; + + scalar_type NG_already_in_reflective_dL_measure; + spectral_type dummyior; + brdf_type ggx = brdf_type::create(A.x, A.y, dummyior, dummyior); + typename brdf_type::params_anisotropic_t brdf_params = typename brdf_type::params_anisotropic_t::create(params._sample, params.interaction, params.cache, params._clamp); + NG_already_in_reflective_dL_measure = ggx.__eval_DG_wo_clamps(brdf_params); + + ndf::microfacet_to_light_measure_transform,ndf::REFLECT_REFRACT_BIT> microfacet_transform = + ndf::microfacet_to_light_measure_transform,ndf::REFLECT_REFRACT_BIT>::create(NG_already_in_reflective_dL_measure,params.getNdotL(),transmitted,params.getVdotH(),params.getLdotH(),VdotHLdotH,orientedEta.value); + scalar_type f = fresnel::Dielectric::__call(orientedEta2, nbl::hlsl::abs(params.getVdotH())); + return hlsl::promote(f) * microfacet_transform(); + } + + sample_type __generate_wo_clamps(NBL_CONST_REF_ARG(vector3_type) localV, NBL_CONST_REF_ARG(vector3_type) H, NBL_CONST_REF_ARG(matrix3x3_type) m, NBL_REF_ARG(vector3_type) u, NBL_CONST_REF_ARG(fresnel::OrientedEtas) orientedEta, NBL_CONST_REF_ARG(fresnel::OrientedEtaRcps) rcpEta, NBL_REF_ARG(anisocache_type) cache) + { + const scalar_type localVdotH = nbl::hlsl::dot(localV,H); + const scalar_type reflectance = fresnel::Dielectric::__call(orientedEta.value * orientedEta.value,nbl::hlsl::abs(localVdotH)); + + scalar_type rcpChoiceProb; + bool transmitted = math::partitionRandVariable(reflectance, u.z, rcpChoiceProb); + + cache = anisocache_type::create(localV, H); + + const scalar_type VdotH = cache.iso_cache.getVdotH(); + Refract r; + r.recomputeNdotT(VdotH < 0.0, VdotH * VdotH, rcpEta.value2); + cache.iso_cache.LdotH = hlsl::mix(VdotH, r.NdotT, transmitted); + ray_dir_info_type localL; + bxdf::ReflectRefract rr = bxdf::ReflectRefract::create(transmitted, localV, H, VdotH, cache.iso_cache.getLdotH(), rcpEta.value); + localL.direction = rr(transmitted); + + return sample_type::createFromTangentSpace(localV, localL, m); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u, NBL_REF_ARG(anisocache_type) cache) + { + const vector3_type localV = interaction.getTangentSpaceV(); + + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(interaction.isotropic.getNdotV(), eta); + fresnel::OrientedEtaRcps rcpEta = fresnel::OrientedEtaRcps::create(interaction.isotropic.getNdotV(), eta); + + const vector3_type upperHemisphereV = orientedEta.backside ? -localV : localV; + + spectral_type dummyior; + brdf_type ggx = brdf_type::create(A.x, A.y, dummyior, dummyior); + const vector3_type H = ggx.__generate(upperHemisphereV, u.xy); + + return __generate_wo_clamps(localV, H, interaction.getFromTangentSpace(), u, orientedEta, rcpEta, cache); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u) + { + anisocache_type dummycache; + return generate(interaction, u, dummycache); + } + + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u, NBL_REF_ARG(isocache_type) cache) + { + anisocache_type anisocache; + sample_type s = generate(anisotropic_interaction_type::create(interaction), u, anisocache); + cache = anisocache.iso_cache; + return s; + } + + scalar_type pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(params.getVdotH(), eta); + const scalar_type orientedEta2 = orientedEta.value * orientedEta.value; + + const scalar_type VdotHLdotH = params.getVdotH() * params.getLdotH(); + const bool transmitted = VdotHLdotH < 0.0; + + const scalar_type reflectance = fresnel::Dielectric::__call(orientedEta2, nbl::hlsl::abs(params.getVdotH())); + + scalar_type ndf, devsh_v; + const scalar_type a2 = A.x*A.x; + ndf::SIsotropicParams ndfparams = ndf::SIsotropicParams::create(a2, params.getNdotH(), params.getNdotH2()); + ndf::GGX ggx_ndf; + ndf = ggx_ndf(ndfparams); + + smith::GGX ggx_smith; + devsh_v = ggx_smith.devsh_part(params.getNdotV2(), a2, 1.0-a2); + const scalar_type lambda = ggx_smith.G1_wo_numerator(params.getNdotV(), devsh_v); + + smith::bsdf::VNDF_pdf > vndf = smith::bsdf::VNDF_pdf >::create(ndf, params.getNdotV()); + return vndf(lambda, transmitted, params.getVdotH(), params.getLdotH(), VdotHLdotH, orientedEta.value, reflectance); + } + scalar_type pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(params.getVdotH(), eta); + const scalar_type orientedEta2 = orientedEta.value * orientedEta.value; + + const scalar_type VdotHLdotH = params.getVdotH() * params.getLdotH(); + const bool transmitted = VdotHLdotH < 0.0; + + const scalar_type reflectance = fresnel::Dielectric::__call(orientedEta2, nbl::hlsl::abs(params.getVdotH())); + + scalar_type ndf, devsh_v; + const scalar_type ax2 = A.x*A.x; + const scalar_type ay2 = A.y*A.y; + + ndf::SAnisotropicParams ndfparams = ndf::SAnisotropicParams::create(A.x, A.y, ax2, ay2, params.getTdotH2(), params.getBdotH2(), params.getNdotH2()); + ndf::GGX ggx_ndf; + ndf = ggx_ndf(ndfparams); + + smith::GGX ggx_smith; + devsh_v = ggx_smith.devsh_part(params.getTdotV2(), params.getBdotV2(), params.getNdotV2(), ax2, ay2); + const scalar_type lambda = ggx_smith.G1_wo_numerator(params.getNdotV(), devsh_v); + + smith::bsdf::VNDF_pdf > vndf = smith::bsdf::VNDF_pdf >::create(ndf, params.getNdotV()); + return vndf(lambda, transmitted, params.getVdotH(), params.getLdotH(), VdotHLdotH, orientedEta.value, reflectance); + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + const scalar_type ax2 = A.x*A.x; + const scalar_type ay2 = A.y*A.y; + + scalar_type _pdf = pdf(params); + + smith::GGX ggx_smith; + scalar_type quo; + smith::SIsotropicParams smithparams = smith::SIsotropicParams::create(ax2, params.getNdotV(), params.getNdotV2(), params.getNdotL(), params.getNdotL2()); + quo = ggx_smith.G2_over_G1(smithparams); + + return quotient_pdf_type::create((spectral_type)(quo), _pdf); + } + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + const scalar_type ax2 = A.x*A.x; + const scalar_type ay2 = A.y*A.y; + + scalar_type _pdf = pdf(params); + + smith::GGX ggx_smith; + scalar_type quo; + smith::SAnisotropicParams smithparams = smith::SAnisotropicParams::create(ax2, ay2, params.getNdotV(), params.getTdotV2(), params.getBdotV2(), params.getNdotV2(), params.getNdotL(), params.getTdotL2(), params.getBdotL2(), params.getNdotL2()); + quo = ggx_smith.G2_over_G1(smithparams); + + return quotient_pdf_type::create((spectral_type)(quo), _pdf); + } + + vector2_type A; + scalar_type eta; +}; + +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/lambertian.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/lambertian.hlsl new file mode 100644 index 0000000000..668ccd4bf0 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/transmission/lambertian.hlsl @@ -0,0 +1,176 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_BXDF_TRANSMISSION_LAMBERTIAN_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_TRANSMISSION_LAMBERTIAN_INCLUDED_ + +#include "nbl/builtin/hlsl/bxdf/common.hlsl" +#include "nbl/builtin/hlsl/sampling/cos_weighted.hlsl" +#include "nbl/builtin/hlsl/bxdf/reflection.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace transmission +{ + +template +struct LambertianParams; + +template +NBL_PARTIAL_REQ_TOP(!surface_interactions::Anisotropic) +struct LambertianParams) > +{ + using this_t = LambertianParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + + LS _sample; + SI interaction; + BxDFClampMode _clamp; +}; +template +NBL_PARTIAL_REQ_TOP(surface_interactions::Anisotropic) +struct LambertianParams) > +{ + using this_t = LambertianParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + + // aniso + Scalar getTdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getTdotL() * _sample.getTdotL(); } + Scalar getBdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getBdotL() * _sample.getBdotL(); } + Scalar getTdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getTdotV() * interaction.getTdotV(); } + Scalar getBdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getBdotV() * interaction.getBdotV(); } + + LS _sample; + SI interaction; + BxDFClampMode _clamp; +}; + +template && surface_interactions::Isotropic && surface_interactions::Anisotropic) +struct SLambertianBxDF +{ + using this_t = SLambertianBxDF; + using scalar_type = typename LS::scalar_type; + using ray_dir_info_type = typename LS::ray_dir_info_type; + using isotropic_interaction_type = Iso; + using anisotropic_interaction_type = Aniso; + using sample_type = LS; + using spectral_type = Spectrum; + using quotient_pdf_type = sampling::quotient_and_pdf; + + using params_isotropic_t = LambertianParams; + using params_anisotropic_t = LambertianParams; + + + static this_t create() + { + this_t retval; + // nothing here, just keeping convention with others + return retval; + } + + static this_t create(NBL_CONST_REF_ARG(SBxDFCreationParams) params) + { + return create(); + } + + void init(NBL_CONST_REF_ARG(SBxDFCreationParams) params) + { + // do nothing + } + + scalar_type __eval_pi_factored_out(scalar_type absNdotL) + { + return absNdotL; + } + + scalar_type eval(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + return __eval_pi_factored_out(params.getNdotL()) * numbers::inv_pi * 0.5; + } + scalar_type eval(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + return __eval_pi_factored_out(params.getNdotL()) * numbers::inv_pi * 0.5; + } + + sample_type generate_wo_clamps(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(vector) u) + { + ray_dir_info_type L; + L.direction = sampling::ProjectedSphere::generate(u); + return sample_type::createFromTangentSpace(interaction.getTangentSpaceV(), L, interaction.getFromTangentSpace()); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(vector) u) + { + return generate_wo_clamps(interaction, u); + } + + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(vector) u) + { + return generate_wo_clamps(anisotropic_interaction_type::create(interaction), u); + } + + scalar_type pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + return sampling::ProjectedSphere::pdf(params.getNdotL()); + } + scalar_type pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + return sampling::ProjectedSphere::pdf(params.getNdotL()); + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + sampling::quotient_and_pdf qp = sampling::ProjectedSphere::template quotient_and_pdf(params.getNdotL()); + return quotient_pdf_type::create(hlsl::promote(qp.quotient), qp.pdf); + } + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + sampling::quotient_and_pdf qp = sampling::ProjectedSphere::template quotient_and_pdf(params.getNdotL()); + return quotient_pdf_type::create(hlsl::promote(qp.quotient), qp.pdf); + } +}; + +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl new file mode 100644 index 0000000000..3935eb62b2 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl @@ -0,0 +1,332 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_BXDF_TRANSMISSION_SMOOTH_DIELECTRIC_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_TRANSMISSION_SMOOTH_DIELECTRIC_INCLUDED_ + +#include "nbl/builtin/hlsl/bxdf/common.hlsl" +#include "nbl/builtin/hlsl/sampling/cos_weighted.hlsl" +#include "nbl/builtin/hlsl/bxdf/reflection.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace transmission +{ + +template +struct SmoothDielectricParams; + +template +NBL_PARTIAL_REQ_TOP(!surface_interactions::Anisotropic) +struct SmoothDielectricParams) > +{ + using this_t = SmoothDielectricParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + + LS _sample; + SI interaction; + BxDFClampMode _clamp; +}; +template +NBL_PARTIAL_REQ_TOP(surface_interactions::Anisotropic) +struct SmoothDielectricParams) > +{ + using this_t = SmoothDielectricParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + + // aniso + Scalar getTdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getTdotL() * _sample.getTdotL(); } + Scalar getBdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getBdotL() * _sample.getBdotL(); } + Scalar getTdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getTdotV() * interaction.getTdotV(); } + Scalar getBdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getBdotV() * interaction.getBdotV(); } + + LS _sample; + SI interaction; + BxDFClampMode _clamp; +}; + +template // NBL_FUNC_REQUIRES(Sample && IsotropicMicrofacetCache && AnisotropicMicrofacetCache) // dxc won't let me put this in +struct SSmoothDielectricBxDF; + +template +struct SSmoothDielectricBxDF +{ + using this_t = SSmoothDielectricBxDF; + using scalar_type = typename LS::scalar_type; + using ray_dir_info_type = typename LS::ray_dir_info_type; + using vector3_type = vector; + + using isotropic_interaction_type = Iso; + using anisotropic_interaction_type = Aniso; + using sample_type = LS; + using spectral_type = Spectrum; + using quotient_pdf_type = sampling::quotient_and_pdf; + using isocache_type = IsoCache; + using anisocache_type = AnisoCache; + + using params_isotropic_t = SmoothDielectricParams; + using params_anisotropic_t = SmoothDielectricParams; + + + static this_t create(scalar_type eta) + { + this_t retval; + retval.eta = eta; + return retval; + } + + static this_t create(NBL_CONST_REF_ARG(SBxDFCreationParams) params) + { + return create(params.eta); + } + + void init(NBL_CONST_REF_ARG(SBxDFCreationParams) params) + { + eta = params.eta; + } + + spectral_type eval(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + return (spectral_type)0; + } + spectral_type eval(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + return (spectral_type)0; + } + + sample_type __generate_wo_clamps(NBL_CONST_REF_ARG(vector3_type) V, NBL_CONST_REF_ARG(vector3_type) T, NBL_CONST_REF_ARG(vector3_type) B, NBL_CONST_REF_ARG(vector3_type) N, scalar_type NdotV, scalar_type absNdotV, NBL_REF_ARG(vector3_type) u, NBL_CONST_REF_ARG(fresnel::OrientedEtas) orientedEta, NBL_CONST_REF_ARG(fresnel::OrientedEtaRcps) rcpEta, NBL_REF_ARG(bool) transmitted) + { + const scalar_type reflectance = fresnel::Dielectric::__call(orientedEta.value*orientedEta.value, absNdotV); + + scalar_type rcpChoiceProb; + transmitted = math::partitionRandVariable(reflectance, u.z, rcpChoiceProb); + + ray_dir_info_type L; + Refract r = Refract::create(rcpEta, V, N, NdotV); + bxdf::ReflectRefract rr = bxdf::ReflectRefract::create(transmitted, r); + L.direction = rr(transmitted); + return sample_type::create(L, nbl::hlsl::dot(V, L.direction), T, B, N); + } + + sample_type generate_wo_clamps(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(vector) u) + { + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(interaction.isotropic.getNdotV(), eta); + fresnel::OrientedEtaRcps rcpEta = fresnel::OrientedEtaRcps::create(interaction.isotropic.getNdotV(), eta); + scalar_type NdotV = interaction.isotropic.getNdotV(); + bool dummy; + return __generate_wo_clamps(interaction.isotropic.getV().getDirection(), interaction.getT(), interaction.getB(), interaction.isotropic.getN(), NdotV, + NdotV, u, orientedEta, rcpEta, dummy); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(vector) u) + { + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(interaction.isotropic.getNdotV(), eta); + fresnel::OrientedEtaRcps rcpEta = fresnel::OrientedEtaRcps::create(interaction.isotropic.getNdotV(), eta); + scalar_type NdotV = interaction.isotropic.getNdotV(); + bool dummy; + return __generate_wo_clamps(interaction.isotropic.getV().getDirection(), interaction.getT(), interaction.getB(), interaction.isotropic.getN(), NdotV, + nbl::hlsl::abs(NdotV), u, orientedEta, rcpEta, dummy); + } + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_REF_ARG(vector) u) + { + return generate(anisotropic_interaction_type::create(interaction), u); + } + + // eval and pdf return 0 because smooth dielectric/conductor BxDFs are dirac delta distributions, model perfectly specular objects that scatter light to only one outgoing direction + scalar_type pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + return 0; + } + scalar_type pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + return 0; + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + const bool transmitted = ComputeMicrofacetNormal::isTransmissionPath(params.getNdotVUnclamped(), params.getNdotLUnclamped()); + + fresnel::OrientedEtaRcps rcpOrientedEtas = fresnel::OrientedEtaRcps::create(params.getNdotV(), eta); + + const scalar_type _pdf = bit_cast(numeric_limits::infinity); + scalar_type quo = hlsl::mix(1.0, rcpOrientedEtas.value, transmitted); + return quotient_pdf_type::create((spectral_type)(quo), _pdf); + } + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + const bool transmitted = ComputeMicrofacetNormal::isTransmissionPath(params.getNdotVUnclamped(), params.getNdotLUnclamped()); + + fresnel::OrientedEtaRcps rcpOrientedEtas = fresnel::OrientedEtaRcps::create(params.getNdotV(), eta); + + const scalar_type _pdf = bit_cast(numeric_limits::infinity); + scalar_type quo = hlsl::mix(1.0, rcpOrientedEtas.value, transmitted); + return quotient_pdf_type::create((spectral_type)(quo), _pdf); + } + + scalar_type eta; +}; + +template +struct SSmoothDielectricBxDF +{ + using this_t = SSmoothDielectricBxDF; + using scalar_type = typename LS::scalar_type; + using ray_dir_info_type = typename LS::ray_dir_info_type; + using vector3_type = vector; + + using isotropic_interaction_type = Iso; + using anisotropic_interaction_type = Aniso; + using sample_type = LS; + using spectral_type = Spectrum; + using quotient_pdf_type = sampling::quotient_and_pdf; + using isocache_type = IsoCache; + using anisocache_type = AnisoCache; + + using params_isotropic_t = SmoothDielectricParams; + using params_anisotropic_t = SmoothDielectricParams; + + + static this_t create(NBL_CONST_REF_ARG(spectral_type) eta2, NBL_CONST_REF_ARG(spectral_type) luminosityContributionHint) + { + this_t retval; + retval.eta2 = eta2; + retval.luminosityContributionHint = luminosityContributionHint; + return retval; + } + + static this_t create(NBL_CONST_REF_ARG(SBxDFCreationParams) params) + { + return create(params.eta2, params.luminosityContributionHint); + } + + void init(NBL_CONST_REF_ARG(SBxDFCreationParams) params) + { + eta2 = params.eta2; + luminosityContributionHint = params.luminosityContributionHint; + } + + spectral_type eval(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + return (spectral_type)0; + } + spectral_type eval(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + return (spectral_type)0; + } + + // usually `luminosityContributionHint` would be the Rec.709 luma coefficients (the Y row of the RGB to CIE XYZ matrix) + // its basically a set of weights that determine + // assert(1.0==luminosityContributionHint.r+luminosityContributionHint.g+luminosityContributionHint.b); + // `remainderMetadata` is a variable which the generator function returns byproducts of sample generation that would otherwise have to be redundantly calculated `quotient_and_pdf` + sample_type __generate_wo_clamps(NBL_CONST_REF_ARG(vector3_type) V, NBL_CONST_REF_ARG(vector3_type) T, NBL_CONST_REF_ARG(vector3_type) B, NBL_CONST_REF_ARG(vector3_type) N, scalar_type NdotV, scalar_type absNdotV, NBL_REF_ARG(vector3_type) u, NBL_CONST_REF_ARG(spectral_type) eta2, NBL_CONST_REF_ARG(spectral_type) luminosityContributionHint, NBL_REF_ARG(spectral_type) remainderMetadata) + { + // we will only ever intersect from the outside + const spectral_type reflectance = fresnel::thinDielectricInfiniteScatter(fresnel::Dielectric::__call(eta2,absNdotV)); + + // we are only allowed one choice for the entire ray, so make the probability a weighted sum + const scalar_type reflectionProb = nbl::hlsl::dot(reflectance, luminosityContributionHint); + + scalar_type rcpChoiceProb; + const bool transmitted = math::partitionRandVariable(reflectionProb, u.z, rcpChoiceProb); + remainderMetadata = (transmitted ? ((spectral_type)(1.0) - reflectance) : reflectance) * rcpChoiceProb; + + ray_dir_info_type L; + L.direction = (transmitted ? (vector3_type)(0.0) : N * 2.0f * NdotV) - V; + return sample_type::create(L, nbl::hlsl::dot(V, L.direction), T, B, N); + } + + sample_type generate_wo_clamps(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(vector) u) + { + scalar_type NdotV = interaction.isotropic.getNdotV(); + vector3_type dummy; + return __generate_wo_clamps(interaction.isotropic.getV().getDirection(), interaction.getT(), interaction.getB(), interaction.isotropic.getN(), NdotV, NdotV, u, eta2, luminosityContributionHint, dummy); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(vector) u) + { + scalar_type NdotV = interaction.isotropic.getNdotV(); + vector3_type dummy; + return __generate_wo_clamps(interaction.isotropic.getV().getDirection(), interaction.getT(), interaction.getB(), interaction.isotropic.getN(), NdotV, nbl::hlsl::abs(NdotV), u, eta2, luminosityContributionHint, dummy); + } + + scalar_type pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + return 0; + } + scalar_type pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + return 0; + } + + // isotropic only? + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + const bool transmitted = ComputeMicrofacetNormal::isTransmissionPath(params.getNdotVUnclamped(), params.getNdotLUnclamped()); + const spectral_type reflectance = fresnel::thinDielectricInfiniteScatter(fresnel::Dielectric::__call(eta2, params.getNdotV())); + const spectral_type sampleValue = hlsl::mix(reflectance, (spectral_type)(1.0) - reflectance, transmitted); + + const scalar_type sampleProb = nbl::hlsl::dot(sampleValue,luminosityContributionHint); + + const scalar_type _pdf = bit_cast(numeric_limits::infinity); + return quotient_pdf_type::create((spectral_type)(sampleValue / sampleProb), _pdf); + } + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + const bool transmitted = ComputeMicrofacetNormal::isTransmissionPath(params.getNdotVUnclamped(), params.getNdotLUnclamped()); + const spectral_type reflectance = fresnel::thinDielectricInfiniteScatter(fresnel::Dielectric::__call(eta2, params.getNdotV())); + const spectral_type sampleValue = hlsl::mix(reflectance, (spectral_type)(1.0) - reflectance, transmitted); + + const scalar_type sampleProb = nbl::hlsl::dot(sampleValue,luminosityContributionHint); + + const scalar_type _pdf = bit_cast(numeric_limits::infinity); + return quotient_pdf_type::create((spectral_type)(sampleValue / sampleProb), _pdf); + } + + spectral_type eta2; + spectral_type luminosityContributionHint; +}; + +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/concepts.hlsl b/include/nbl/builtin/hlsl/concepts.hlsl index 7fd725dc2b..4b82955bb7 100644 --- a/include/nbl/builtin/hlsl/concepts.hlsl +++ b/include/nbl/builtin/hlsl/concepts.hlsl @@ -33,6 +33,7 @@ namespace concepts #define NBL_CONCEPT_REQ_EXPR 1 // #define NBL_CONCEPT_REQ_EXPR_RET_TYPE 2 +#define NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT 3 //! Now diverge @@ -64,8 +65,9 @@ concept NBL_CONCEPT_NAME = requires BOOST_PP_EXPR_IF(LOCAL_PARAM_COUNT,(BOOST_PP #define NBL_IMPL_CONCEPT_REQ_TYPE(...) typename __VA_ARGS__; #define NBL_IMPL_CONCEPT_REQ_EXPR(...) __VA_ARGS__; #define NBL_IMPL_CONCEPT_REQ_EXPR_RET_TYPE(E,C,...) {E}; C; +#define NBL_IMPL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT(C,...) C< __VA_ARGS__ >; // -#define NBL_IMPL_CONCEPT (NBL_IMPL_CONCEPT_REQ_TYPE,NBL_IMPL_CONCEPT_REQ_EXPR,NBL_IMPL_CONCEPT_REQ_EXPR_RET_TYPE) +#define NBL_IMPL_CONCEPT (NBL_IMPL_CONCEPT_REQ_TYPE,NBL_IMPL_CONCEPT_REQ_EXPR,NBL_IMPL_CONCEPT_REQ_EXPR_RET_TYPE,NBL_IMPL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT) // #define NBL_IMPL_CONCEPT_END_DEF(r,unused,i,e) NBL_EVAL(BOOST_PP_TUPLE_ELEM(BOOST_PP_SEQ_HEAD(e),NBL_IMPL_CONCEPT) BOOST_PP_SEQ_TAIL(e)) // @@ -95,8 +97,9 @@ concept NBL_CONCEPT_NAME = requires BOOST_PP_EXPR_IF(LOCAL_PARAM_COUNT,(BOOST_PP #define NBL_IMPL_CONCEPT_REQ_TYPE(...) ::nbl::hlsl::make_void_t #define NBL_IMPL_CONCEPT_REQ_EXPR(...) ::nbl::hlsl::make_void_t #define NBL_IMPL_CONCEPT_REQ_EXPR_RET_TYPE(E,C,...) ::nbl::hlsl::enable_if_t > +#define NBL_IMPL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT(C,...) ::nbl::hlsl::enable_if_t > // -#define NBL_IMPL_CONCEPT_SFINAE (NBL_IMPL_CONCEPT_REQ_TYPE,NBL_IMPL_CONCEPT_REQ_EXPR,NBL_IMPL_CONCEPT_REQ_EXPR_RET_TYPE) +#define NBL_IMPL_CONCEPT_SFINAE (NBL_IMPL_CONCEPT_REQ_TYPE,NBL_IMPL_CONCEPT_REQ_EXPR,NBL_IMPL_CONCEPT_REQ_EXPR_RET_TYPE,NBL_IMPL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT) // #define NBL_IMPL_CONCEPT_END_DEF(r,unused,i,e) template \ struct BOOST_PP_CAT(__requirement,i) : ::nbl::hlsl::false_type {}; \ diff --git a/include/nbl/builtin/hlsl/concepts/core.hlsl b/include/nbl/builtin/hlsl/concepts/core.hlsl index c1bc0277df..dcbafae8a5 100644 --- a/include/nbl/builtin/hlsl/concepts/core.hlsl +++ b/include/nbl/builtin/hlsl/concepts/core.hlsl @@ -47,7 +47,7 @@ template NBL_BOOL_CONCEPT UnsignedIntegralScalar = !nbl::hlsl::is_signed_v && ::nbl::hlsl::is_integral_v && nbl::hlsl::is_scalar_v; template -NBL_BOOL_CONCEPT FloatingPointScalar = (nbl::hlsl::is_floating_point_v && nbl::hlsl::is_scalar_v); +NBL_BOOL_CONCEPT FloatingPointScalar = nbl::hlsl::is_floating_point_v && nbl::hlsl::is_scalar_v; template NBL_BOOL_CONCEPT BooleanScalar = concepts::Boolean && nbl::hlsl::is_scalar_v; diff --git a/include/nbl/builtin/hlsl/cpp_compat/impl/intrinsics_impl.hlsl b/include/nbl/builtin/hlsl/cpp_compat/impl/intrinsics_impl.hlsl index 431ea625bf..bdc31b003d 100644 --- a/include/nbl/builtin/hlsl/cpp_compat/impl/intrinsics_impl.hlsl +++ b/include/nbl/builtin/hlsl/cpp_compat/impl/intrinsics_impl.hlsl @@ -141,8 +141,8 @@ template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(length_helper, length, template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(normalize_helper, normalize, (T), (T), T) template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(rsqrt_helper, inverseSqrt, (T), (T), T) template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(fract_helper, fract, (T), (T), T) -template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(all_helper, any, (T), (T), T) -template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(any_helper, any, (T), (T), T) +template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(all_helper, any, (T), (T), bool) +template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(any_helper, any, (T), (T), bool) template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(sign_helper, fSign, (T), (T), T) template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(sign_helper, sSign, (T), (T), T) template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(radians_helper, radians, (T), (T), T) @@ -241,8 +241,8 @@ struct mix_helper(e }; template -NBL_PARTIAL_REQ_TOP((concepts::Scalar || concepts::Vectorial) && !concepts::Boolean && concepts::Boolean) -struct mix_helper || concepts::Vectorial) && !concepts::Boolean && concepts::Boolean) > +NBL_PARTIAL_REQ_TOP(concepts::Boolean && ((concepts::Vector && concepts::Vector && vector_traits::Dimension==vector_traits::Dimension) || concepts::Scalar)) +struct mix_helper && ((concepts::Vector && concepts::Vector && vector_traits::Dimension==vector_traits::Dimension) || concepts::Scalar)) > { using return_t = conditional_t, vector::scalar_type, vector_traits::Dimension>, T>; // for a component of a that is false, the corresponding component of x is returned @@ -865,6 +865,25 @@ struct mix_helper } }; +template +NBL_PARTIAL_REQ_TOP(VECTOR_SPECIALIZATION_CONCEPT&& concepts::Boolean&& !(vector_traits::Dimension == vector_traits::Dimension) && concepts::BooleanScalar) +struct mix_helper&& !(vector_traits::Dimension == vector_traits::Dimension) && concepts::BooleanScalar) > +{ + using return_t = T; + static return_t __call(NBL_CONST_REF_ARG(T) x, NBL_CONST_REF_ARG(T) y, NBL_CONST_REF_ARG(U) a) + { + using traitsT = hlsl::vector_traits; + array_get getterT; + array_set setter; + + return_t output; + for (uint32_t i = 0; i < traitsT::Dimension; ++i) + setter(output, i, mix_helper::__call(getterT(x, i), getterT(y, i), a)); + + return output; + } +}; + template NBL_PARTIAL_REQ_TOP(VECTOR_SPECIALIZATION_CONCEPT && concepts::Boolean && vector_traits::Dimension == vector_traits::Dimension) struct mix_helper && vector_traits::Dimension == vector_traits::Dimension) > diff --git a/include/nbl/builtin/hlsl/cpp_compat/matrix.hlsl b/include/nbl/builtin/hlsl/cpp_compat/matrix.hlsl index b1d33f097b..bc78154bfa 100644 --- a/include/nbl/builtin/hlsl/cpp_compat/matrix.hlsl +++ b/include/nbl/builtin/hlsl/cpp_compat/matrix.hlsl @@ -15,9 +15,9 @@ struct matrix final : private glm::mat using Base::Base; using Base::operator[]; - // For assigning to same dimension use implicit ctor, and even then only allow for dimension truncation - template requires ((X!=N || Y!=M) && X>=N && Y>=M) - explicit matrix(matrix const& m) : Base(reinterpret_cast const&>(m)) {} + // For assigning to same dimension and type use implicit ctor, and even then only allow for dimension truncation + template requires ((!std::is_same_v || X!=N || Y!=M) && X>=N && Y>=M) + explicit matrix(matrix const& m) : Base(reinterpret_cast const&>(m)) {} matrix(matrix const&) = default; explicit matrix(Base const& base) : Base(base) {} diff --git a/include/nbl/builtin/hlsl/glsl_compat/core.hlsl b/include/nbl/builtin/hlsl/glsl_compat/core.hlsl index 337db5e22f..88d2f4b7a6 100644 --- a/include/nbl/builtin/hlsl/glsl_compat/core.hlsl +++ b/include/nbl/builtin/hlsl/glsl_compat/core.hlsl @@ -8,6 +8,9 @@ #include "nbl/builtin/hlsl/spirv_intrinsics/core.hlsl" #include "nbl/builtin/hlsl/type_traits.hlsl" #include "nbl/builtin/hlsl/spirv_intrinsics/glsl.std.450.hlsl" +#include "nbl/builtin/hlsl/concepts/core.hlsl" +#include "nbl/builtin/hlsl/concepts/vector.hlsl" +#include "nbl/builtin/hlsl/concepts/matrix.hlsl" namespace nbl { @@ -234,6 +237,68 @@ T bitfieldReverse(T value) #endif +namespace impl +{ +template +struct equal_helper; + +#ifdef __HLSL_VERSION + +template +NBL_PARTIAL_REQ_TOP(concepts::Vectorial && concepts::Integral) +struct equal_helper && concepts::Integral) > +{ + using return_t = vector::Dimension>; + + static return_t __call(const Vectorial lhs, const Vectorial rhs) + { + return spirv::IEqual(lhs, rhs); + } +}; + +template +NBL_PARTIAL_REQ_TOP(concepts::Vectorial && concepts::FloatingPoint) +struct equal_helper && concepts::FloatingPoint) > +{ + using return_t = vector::Dimension>; + + static return_t __call(const Vectorial lhs, const Vectorial rhs) + { + return spirv::FOrdEqual(lhs, rhs); + } +}; + +#else + +template +NBL_PARTIAL_REQ_TOP(concepts::Vectorial) +struct equal_helper) > +{ + using return_t = vector::Dimension>; + + static return_t __call(const Vectorial lhs, const Vectorial rhs) + { + using traits = hlsl::vector_traits; + array_get getter; + array_set setter; + + return_t output; + for (uint32_t i = 0; i < traits::Dimension; ++i) + setter(output, i, getter(lhs, i) == getter(rhs, i)); + + return output; + } +}; + +#endif +} + +template +inline vector::Dimension> equal(NBL_CONST_REF_ARG(T) x, NBL_CONST_REF_ARG(T) y) +{ + return impl::equal_helper::__call(x, y); +} + } } } diff --git a/include/nbl/builtin/hlsl/ieee754.hlsl b/include/nbl/builtin/hlsl/ieee754.hlsl index 4b281c2111..a32ba589f2 100644 --- a/include/nbl/builtin/hlsl/ieee754.hlsl +++ b/include/nbl/builtin/hlsl/ieee754.hlsl @@ -142,13 +142,67 @@ NBL_CONSTEXPR_INLINE_FUNC FloatingPoint copySign(FloatingPoint to, FloatingPoint return bit_cast(toAsUint | extractSignPreserveBitPattern(from)); } -template ) -NBL_CONSTEXPR_INLINE_FUNC FloatingPoint flipSign(FloatingPoint val, bool flip = true) +namespace impl +{ +template +struct flipSign_helper; + +template +NBL_PARTIAL_REQ_TOP(concepts::FloatingPointLikeScalar && concepts::BooleanScalar) +struct flipSign_helper && concepts::BooleanScalar) > +{ + static FloatingPoint __call(FloatingPoint val, Bool flip) + { + using AsFloat = typename float_of_size::type; + using AsUint = typename unsigned_integer_of_size::type; + const AsUint asUint = ieee754::impl::bitCastToUintType(val); + return bit_cast(asUint ^ (flip ? ieee754::traits::signMask : AsUint(0ull))); + } +}; + +template +NBL_PARTIAL_REQ_TOP(concepts::FloatingPointLikeVectorial && concepts::BooleanScalar) +struct flipSign_helper && concepts::BooleanScalar) > { - using AsFloat = typename float_of_size::type; - using AsUint = typename unsigned_integer_of_size::type; - const AsUint asUint = ieee754::impl::bitCastToUintType(val); - return bit_cast(asUint ^ (flip ? ieee754::traits::signMask : AsUint(0ull))); + static Vectorial __call(Vectorial val, Bool flip) + { + using traits = hlsl::vector_traits; + array_get getter; + array_set setter; + + Vectorial output; + for (uint32_t i = 0; i < traits::Dimension; ++i) + setter(output, i, flipSign_helper::__call(getter(val, i), flip)); + + return output; + } +}; + +template +NBL_PARTIAL_REQ_TOP(concepts::FloatingPointLikeVectorial && concepts::Boolean && !concepts::Scalar && vector_traits::Dimension==vector_traits::Dimension) +struct flipSign_helper && concepts::Boolean && !concepts::Scalar && vector_traits::Dimension==vector_traits::Dimension) > +{ + static Vectorial __call(Vectorial val, BoolVector flip) + { + using traits_v = hlsl::vector_traits; + using traits_f = hlsl::vector_traits; + array_get getter_v; + array_get getter_f; + array_set setter; + + Vectorial output; + for (uint32_t i = 0; i < traits_v::Dimension; ++i) + setter(output, i, flipSign_helper::__call(getter_v(val, i), getter_f(flip, i))); + + return output; + } +}; +} + +template +NBL_CONSTEXPR_INLINE_FUNC T flipSign(T val, U flip) +{ + return impl::flipSign_helper::__call(val, flip); } } diff --git a/include/nbl/builtin/hlsl/limits.hlsl b/include/nbl/builtin/hlsl/limits.hlsl index 146957dc3e..2cb175a5fd 100644 --- a/include/nbl/builtin/hlsl/limits.hlsl +++ b/include/nbl/builtin/hlsl/limits.hlsl @@ -129,7 +129,7 @@ struct num_base : type_identity NBL_CONSTEXPR_STATIC_INLINE int32_t float_max_decimal_exponent = 4*S16 + 30*S32 + 232*S64; NBL_CONSTEXPR_STATIC_INLINE int32_t float_exponent_bits = 8 * size - 1 - (float_digits-1); - NBL_CONSTEXPR_STATIC_INLINE int32_t float_max_exponent = 1 << (float_exponent_bits-1); + NBL_CONSTEXPR_STATIC_INLINE int32_t float_max_exponent = int32_t(1) << (float_exponent_bits-1); NBL_CONSTEXPR_STATIC_INLINE int32_t float_min_exponent = 3 - float_max_exponent; NBL_CONSTEXPR_STATIC_INLINE bool is_bool = is_same::value; diff --git a/include/nbl/builtin/hlsl/math/functions.hlsl b/include/nbl/builtin/hlsl/math/functions.hlsl index be341b6a12..f7a84005e8 100644 --- a/include/nbl/builtin/hlsl/math/functions.hlsl +++ b/include/nbl/builtin/hlsl/math/functions.hlsl @@ -163,7 +163,7 @@ struct conditionalAbsOrMax_helper::scalar_type)>; + using UintOfTSize = unsigned_integer_of_size_t::scalar_type)>; const int dimensionOfT = vector_traits::Dimension; using Uint32VectorWithDimensionOfT = vector; using scalar_type = typename vector_traits::scalar_type; diff --git a/include/nbl/builtin/hlsl/math/linalg/fast_affine.hlsl b/include/nbl/builtin/hlsl/math/linalg/fast_affine.hlsl index d4b2ea5b7e..a8134ee871 100644 --- a/include/nbl/builtin/hlsl/math/linalg/fast_affine.hlsl +++ b/include/nbl/builtin/hlsl/math/linalg/fast_affine.hlsl @@ -4,70 +4,180 @@ #ifndef _NBL_BUILTIN_HLSL_MATH_LINALG_FAST_AFFINE_INCLUDED_ #define _NBL_BUILTIN_HLSL_MATH_LINALG_FAST_AFFINE_INCLUDED_ + +#include +#include #include -#if 0 // TODO -vec4 pseudoMul4x4with3x1(in mat4 m, in vec3 v) + +namespace nbl { - return m[0] * v.x + m[1] * v.y + m[2] * v.z + m[3]; -} -vec3 pseudoMul3x4with3x1(in mat4x3 m, in vec3 v) +namespace hlsl { - return m[0] * v.x + m[1] * v.y + m[2] * v.z + m[3]; -} -mat4x3 pseudoMul4x3with4x3(in mat4x3 lhs, in mat4x3 rhs) // TODO: change name to 3x4with3x4 +namespace math { - mat4x3 result; - for (int i = 0; i < 4; i++) - result[i] = lhs[0] * rhs[i][0] + lhs[1] * rhs[i][1] + lhs[2] * rhs[i][2]; - result[3] += lhs[3]; - return result; -} -mat4 pseudoMul4x4with4x3(in mat4 proj, in mat4x3 tform) +namespace linalg { - mat4 result; - for (int i = 0; i < 4; i++) - result[i] = proj[0] * tform[i][0] + proj[1] * tform[i][1] + proj[2] * tform[i][2]; - result[3] += proj[3]; - return result; -} +// TODO: move to macros +#ifdef __HLSL_VERSION +#define NBL_UNROLL [[unroll]] +#else +#define NBL_UNROLL +#endif -// useful for fast computation of a Normal Matrix (you just need to remember to normalize the transformed normal because of the missing divide by the determinant) -mat3 sub3x3TransposeCofactors(in mat3 sub3x3) +// Multiply matrices as-if extended to be filled with identity elements +template +matrix promoted_mul(NBL_CONST_REF_ARG(matrix) lhs, NBL_CONST_REF_ARG(matrix) rhs) { - return mat3( - cross(sub3x3[1],sub3x3[2]), - cross(sub3x3[2],sub3x3[0]), - cross(sub3x3[0],sub3x3[1]) - ); + matrix retval; + // NxM = NxR RxM + // out[i][j] == dot(row[i],col[j]) + // out[i][j] == lhs[i][0]*col[j][0]+...+lhs[i][3]*col[j][3] + // col[a][b] == (rhs^T)[b][a] + // out[i][j] == lhs[i][0]*rhs[0][j]+...+lhs[i][3]*rhs[3][j] + // out[i] == lhs[i][0]*rhs[0]+...+lhs[i][3]*rhs[3] + NBL_UNROLL for (uint32_t i=0; i acc = i >(0); + if (i>=Q) + acc[i] = T(1); + // multiply if not outside of `lhs` matrix + // otherwise the diagonal element is just unity + if (i +vector promoted_mul(NBL_CONST_REF_ARG(matrix) lhs, const vector v) { - sub3x3TransposeCofactors = sub3x3TransposeCofactors(sub3x3); - return floatBitsToUint(dot(sub3x3[0],sub3x3TransposeCofactors[0]))&0x80000000u; + vector retval; + // Nx1 = NxM Mx1 + { + matrix rhs; + // one can safely discard elements of `v[i]` where `i

=M`, because to contribute `lhs` would need to have `M>=P` + NBL_UNROLL for (uint32_t i=0; i tmp = promoted_mul(lhs,rhs); + NBL_UNROLL for (uint32_t i=0; i +struct cofactors_base; -// use this if you anticipate flipped/mirrored models -vec3 fastNormalTransform(in uint signFlipMask, in mat3 sub3x3TransposeCofactors, in vec3 normal) +template +struct cofactors_base { - vec3 tmp = sub3x3TransposeCofactors*normal; - const float tmpLenRcp = inversesqrt(dot(tmp,tmp)); - return tmp*uintBitsToFloat(floatBitsToUint(tmpLenRcp)^signFlipMask); -} -#endif + using matrix_t = matrix; + using vector_t = vector; + + static inline cofactors_base create(NBL_CONST_REF_ARG(matrix_t) val) + { + cofactors_base retval; + + retval.transposed = matrix_t( + hlsl::cross(val[1],val[2]), + hlsl::cross(val[2],val[0]), + hlsl::cross(val[0],val[1]) + ); + + return retval; + } + + // + inline matrix_t get() NBL_CONST_MEMBER_FUNC + { + return hlsl::transpose(transposed); + } + + // + inline vector_t normalTransform(const vector_t n) NBL_CONST_MEMBER_FUNC + { + const vector_t tmp = hlsl::mul(transposed,n); + return hlsl::normalize(tmp); + } + + matrix_t transposed; +}; + +// variant that cares about flipped/mirrored transforms +template +struct cofactors +{ + using pseudo_base_t = cofactors_base; + using matrix_t = typename pseudo_base_t::matrix_t; + using vector_t = typename pseudo_base_t::vector_t; + using mask_t = unsigned_integer_of_size_t; + + static inline cofactors create(NBL_CONST_REF_ARG(matrix_t) val) + { + cofactors retval; + retval.composed = pseudo_base_t::create(val); + + const T det = hlsl::dot(val[0],retval.composed.transposed[0]); + + const mask_t SignBit = 1; + SignBit = SignBit<<(sizeof(mask_t)*8-1); + retval.signFlipMask = bit_cast(det) & SignBit; + + return retval; + } + + // + inline vector_t normalTransform(const vector_t n) NBL_CONST_MEMBER_FUNC + { + const vector_t tmp = hlsl::mul(composed.transposed,n); + const T rcpLen = hlsl::rsqrt(hlsl::dot(tmp,tmp)); + return tmp*bit_cast(bit_cast(rcpLen)^determinantSignMask); + } + + cofactors_base composed; + mask_t determinantSignMask; +}; // -template NBL_REQUIRES(is_matrix_v) // TODO: allow any matrix type AND our emulated ones -Mat3x4 pseudoInverse3x4(NBL_CONST_REF_ARG(Mat3x4) tform) +template) // TODO: allow any matrix type AND our emulated ones +Mat3x4 pseudoInverse3x4(NBL_CONST_REF_ARG(Mat3x4) tform, NBL_CONST_REF_ARG(matrix,3,3>) sub3x3Inv) { - const matrix,3,3> sub3x3Inv = inverse(mat3(tform)); Mat3x4 retval; retval[0] = sub3x3Inv[0]; retval[1] = sub3x3Inv[1]; retval[2] = sub3x3Inv[2]; - retval[3] = -sub3x3Inv*tform[3]; + retval[3] = -hlsl::mul(sub3x3Inv,tform[3]); return retval; } +template) // TODO: allow any matrix type AND our emulated ones +Mat3x4 pseudoInverse3x4(NBL_CONST_REF_ARG(Mat3x4) tform) +{ + return pseudoInverse3x4(tform,inverse(matrix,3,3>(tform))); +} + +} +} +} +} #endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/random/dim_adaptor_recursive.hlsl b/include/nbl/builtin/hlsl/random/dim_adaptor_recursive.hlsl new file mode 100644 index 0000000000..8ec882de78 --- /dev/null +++ b/include/nbl/builtin/hlsl/random/dim_adaptor_recursive.hlsl @@ -0,0 +1,47 @@ +#ifndef _NBL_HLSL_RANDOM_DIM_ADAPTOR_RECURSIVE_INCLUDED_ +#define _NBL_HLSL_RANDOM_DIM_ADAPTOR_RECURSIVE_INCLUDED_ + +#include "nbl/builtin/hlsl/type_traits.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace random +{ + +// adapts PRNG for multiple dimensions using recursive calls, rather than hash based +template +struct DimAdaptorRecursive +{ + using rng_type = RNG; + using return_type = vector; + + static DimAdaptorRecursive construct(rng_type rng) + { + DimAdaptorRecursive retval; + retval.rng = rng; + return retval; + } + + return_type operator()() + { + array_set setter; + + return_type retval; +#ifdef __HLSL_VERSION + [unroll] +#endif + for (uint32_t i = 0; i < DIM; i++) + setter(retval, i, rng()); + return retval; + } + + rng_type rng; +}; + +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/random/lcg.hlsl b/include/nbl/builtin/hlsl/random/lcg.hlsl index 046161bc6b..0ac0d809aa 100644 --- a/include/nbl/builtin/hlsl/random/lcg.hlsl +++ b/include/nbl/builtin/hlsl/random/lcg.hlsl @@ -13,13 +13,15 @@ namespace random struct Lcg { - static Lcg create(const uint32_t _state) + using seed_type = uint32_t; + + static Lcg create(NBL_CONST_REF_ARG(seed_type) _state) { Lcg retval; retval.state = _state; return retval; } - + uint32_t operator()() { uint32_t LCG_A = 1664525u; @@ -30,7 +32,7 @@ struct Lcg return state; } - uint32_t state; + seed_type state; }; } diff --git a/include/nbl/builtin/hlsl/random/pcg.hlsl b/include/nbl/builtin/hlsl/random/pcg.hlsl index 2102b8702c..2b881e29a2 100644 --- a/include/nbl/builtin/hlsl/random/pcg.hlsl +++ b/include/nbl/builtin/hlsl/random/pcg.hlsl @@ -11,24 +11,28 @@ namespace hlsl namespace random { -struct Pcg +struct PCG32 { - static Pcg create(const uint32_t initialState) - { - Pcg retval; - retval.state = initialState; - return retval; - } - - uint32_t operator()() - { - const uint32_t tmp = state * 747796405u + 2891336453u; - const uint32_t word = ((tmp >> ((tmp >> 28u) + 4u)) ^ tmp) * 277803737u; - state = (word >> 22u) ^ word; - return state; + using seed_type = uint32_t; + + static PCG32 construct(NBL_CONST_REF_ARG(seed_type) initialState) + { + PCG32 retval; + retval.state = initialState; + return retval; } - - uint32_t state; + + uint32_t operator()() + { + const seed_type oldState = state; + state = state * 747796405u + 2891336453u; + const uint32_t word = ((oldState >> ((oldState >> 28u) + 4u)) ^ oldState) * 277803737u; + const uint32_t result = (word >> 22u) ^ word; + + return result; + } + + seed_type state; }; } diff --git a/include/nbl/builtin/hlsl/random/xoroshiro.hlsl b/include/nbl/builtin/hlsl/random/xoroshiro.hlsl index 577b89bb95..1a150f1909 100644 --- a/include/nbl/builtin/hlsl/random/xoroshiro.hlsl +++ b/include/nbl/builtin/hlsl/random/xoroshiro.hlsl @@ -1,16 +1,16 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h -#ifndef _NBL_BUILTIN_HLSL_RANDOM_XOROSHIRO_HLSL_INCLUDED_ -#define _NBL_BUILTIN_HLSL_RANDOM_XOROSHIRO_HLSL_INCLUDED_ - -#include - -#include - -namespace nbl -{ -namespace hlsl +// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_RANDOM_XOROSHIRO_HLSL_INCLUDED_ +#define _NBL_BUILTIN_HLSL_RANDOM_XOROSHIRO_HLSL_INCLUDED_ + +#include + +#include + +namespace nbl +{ +namespace hlsl { // TODO //namespace random @@ -18,58 +18,62 @@ namespace hlsl struct Xoroshiro64StateHolder { - void xoroshiro64_state_advance() - { - state[1] ^= state[0]; - state[0] = rotl(state[0], 26u) ^ state[1] ^ (state[1]<<9u); // a, b - state[1] = rotl(state[1], 13u); // c + void xoroshiro64_state_advance() + { + state[1] ^= state[0]; + state[0] = rotl(state[0], 26u) ^ state[1] ^ (state[1]<<9u); // a, b + state[1] = rotl(state[1], 13u); // c } - + uint32_t2 state; -}; - +}; + struct Xoroshiro64Star { + using seed_type = uint32_t2; + // TODO: create - static Xoroshiro64Star construct(NBL_CONST_REF_ARG(uint32_t2) initialState) + static Xoroshiro64Star construct(NBL_CONST_REF_ARG(seed_type) initialState) { Xoroshiro64StateHolder stateHolder = {initialState}; return Xoroshiro64Star(stateHolder); } - + uint32_t operator()() { - const uint32_t result = stateHolder.state[0]*0x9E3779BBu; - stateHolder.xoroshiro64_state_advance(); - + const uint32_t result = stateHolder.state[0]*0x9E3779BBu; + stateHolder.xoroshiro64_state_advance(); + return result; } - + Xoroshiro64StateHolder stateHolder; -}; - +}; + struct Xoroshiro64StarStar { + using seed_type = uint32_t2; + // TODO: create - static Xoroshiro64StarStar construct(NBL_CONST_REF_ARG(uint32_t2) initialState) + static Xoroshiro64StarStar construct(NBL_CONST_REF_ARG(seed_type) initialState) { Xoroshiro64StateHolder stateHolder = {initialState}; return Xoroshiro64StarStar(stateHolder); } - + uint32_t operator()() { - const uint32_t result = rotl(stateHolder.state[0]*0x9E3779BBu,5u)*5u; - stateHolder.xoroshiro64_state_advance(); - + const uint32_t result = rotl(stateHolder.state[0]*0x9E3779BBu,5u)*5u; + stateHolder.xoroshiro64_state_advance(); + return result; } Xoroshiro64StateHolder stateHolder; -}; - -//} -} -} - +}; + +//} +} +} + #endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/sampling/concentric_mapping.hlsl b/include/nbl/builtin/hlsl/sampling/concentric_mapping.hlsl new file mode 100644 index 0000000000..1a5c96b6df --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/concentric_mapping.hlsl @@ -0,0 +1,50 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_SAMPLING_CONCENTRIC_MAPPING_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SAMPLING_CONCENTRIC_MAPPING_INCLUDED_ + +#include "nbl/builtin/hlsl/glsl_compat/core.hlsl" +#include "nbl/builtin/hlsl/tgmath.hlsl" +#include "nbl/builtin/hlsl/math/functions.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace sampling +{ + +template +vector concentricMapping(vector _u) +{ + //map [0;1]^2 to [-1;1]^2 + vector u = 2.0f * _u - hlsl::promote >(1.0); + + vector p; + if (hlsl::all >(glsl::equal(u, hlsl::promote >(0.0)))) + p = hlsl::promote >(0.0); + else + { + T r; + T theta; + if (abs(u.x) > abs(u.y)) { + r = u.x; + theta = 0.25 * numbers::pi * (u.y / u.x); + } else { + r = u.y; + theta = 0.5 * numbers::pi - 0.25 * numbers::pi * (u.x / u.y); + } + + p = r * vector(cos(theta), sin(theta)); + } + + return p; +} + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/sampling/cos_weighted.hlsl b/include/nbl/builtin/hlsl/sampling/cos_weighted.hlsl new file mode 100644 index 0000000000..d80778ed9a --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/cos_weighted.hlsl @@ -0,0 +1,90 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_SAMPLING_COS_WEIGHTED_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SAMPLING_COS_WEIGHTED_INCLUDED_ + +#include "nbl/builtin/hlsl/concepts.hlsl" +#include "nbl/builtin/hlsl/sampling/concentric_mapping.hlsl" +#include "nbl/builtin/hlsl/sampling/quotient_and_pdf.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace sampling +{ + +template) +struct ProjectedHemisphere +{ + using vector_t2 = vector; + using vector_t3 = vector; + + static vector_t3 generate(vector_t2 _sample) + { + vector_t2 p = concentricMapping(_sample * T(0.99999) + T(0.000005)); + T z = hlsl::sqrt(hlsl::max(T(0.0), T(1.0) - p.x * p.x - p.y * p.y)); + return vector_t3(p.x, p.y, z); + } + + static T pdf(T L_z) + { + return L_z * numbers::inv_pi; + } + + template) + static sampling::quotient_and_pdf quotient_and_pdf(T L) + { + return sampling::quotient_and_pdf::create(U(1.0), pdf(L)); + } + + template) + static sampling::quotient_and_pdf quotient_and_pdf(vector_t3 L) + { + return sampling::quotient_and_pdf::create(U(1.0), pdf(L.z)); + } +}; + +template) +struct ProjectedSphere +{ + using vector_t2 = vector; + using vector_t3 = vector; + using hemisphere_t = ProjectedHemisphere; + + static vector_t3 generate(vector_t3 _sample) + { + vector_t3 retval = hemisphere_t::generate(_sample.xy); + const bool chooseLower = _sample.z > T(0.5); + retval.z = chooseLower ? (-retval.z) : retval.z; + if (chooseLower) + _sample.z -= T(0.5); + _sample.z *= T(2.0); + return retval; + } + + static T pdf(T L_z) + { + return T(0.5) * hemisphere_t::pdf(L_z); + } + + template) + static sampling::quotient_and_pdf quotient_and_pdf(T L) + { + return sampling::quotient_and_pdf::create(U(1.0), pdf(L)); + } + + template) + static sampling::quotient_and_pdf quotient_and_pdf(vector_t3 L) + { + return sampling::quotient_and_pdf::create(U(1.0), pdf(L.z)); + } +}; + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/sampling/quotient_and_pdf.hlsl b/include/nbl/builtin/hlsl/sampling/quotient_and_pdf.hlsl new file mode 100644 index 0000000000..0620b2bad6 --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/quotient_and_pdf.hlsl @@ -0,0 +1,50 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_SAMPLING_QUOTIENT_AND_PDF_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SAMPLING_QUOTIENT_AND_PDF_INCLUDED_ + +#include "nbl/builtin/hlsl/concepts/vector.hlsl" +#include "nbl/builtin/hlsl/vector_utils/vector_traits.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace sampling +{ + +template +NBL_BOOL_CONCEPT Spectral = (concepts::Vectorial || concepts::Scalar) && is_floating_point_v::scalar_type>; + +// finally fixed the semantic F-up, value/pdf = quotient not remainder +template && is_floating_point_v

) +struct quotient_and_pdf +{ + using this_t = quotient_and_pdf; + static this_t create(NBL_CONST_REF_ARG(Q) _quotient, NBL_CONST_REF_ARG(P) _pdf) + { + this_t retval; + retval.quotient = _quotient; + retval.pdf = _pdf; + return retval; + } + + Q value() + { + return quotient*pdf; + } + + Q quotient; + P pdf; +}; + +typedef quotient_and_pdf quotient_and_pdf_scalar; +typedef quotient_and_pdf, float32_t> quotient_and_pdf_rgb; + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/sampling/uniform.hlsl b/include/nbl/builtin/hlsl/sampling/uniform.hlsl new file mode 100644 index 0000000000..69a83d5a93 --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/uniform.hlsl @@ -0,0 +1,76 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_SAMPLING_UNIFORM_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SAMPLING_UNIFORM_INCLUDED_ + +#include "nbl/builtin/hlsl/concepts.hlsl" +#include "nbl/builtin/hlsl/numbers.hlsl" +#include "nbl/builtin/hlsl/tgmath.hlsl" +#include "nbl/builtin/hlsl/sampling/quotient_and_pdf.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace sampling +{ + +template) +struct UniformHemisphere +{ + using vector_t2 = vector; + using vector_t3 = vector; + + static vector_t3 generate(vector_t2 _sample) + { + T z = _sample.x; + T r = hlsl::sqrt(hlsl::max(T(0.0), T(1.0) - z * z)); + T phi = T(2.0) * numbers::pi * _sample.y; + return vector_t3(r * hlsl::cos(phi), r * hlsl::sin(phi), z); + } + + static T pdf() + { + return T(1.0) / (T(2.0) * numbers::pi); + } + + template) + static quotient_and_pdf quotient_and_pdf() + { + return quotient_and_pdf::create(U(1.0), pdf()); + } +}; + +template) +struct UniformSphere +{ + using vector_t2 = vector; + using vector_t3 = vector; + + static vector_t3 generate(vector_t2 _sample) + { + T z = T(1.0) - T(2.0) * _sample.x; + T r = hlsl::sqrt(hlsl::max(T(0.0), T(1.0) - z * z)); + T phi = T(2.0) * numbers::pi * _sample.y; + return vector_t3(r * hlsl::cos(phi), r * hlsl::sin(phi), z); + } + + static T pdf() + { + return T(1.0) / (T(4.0) * numbers::pi); + } + + template) + static quotient_and_pdf quotient_and_pdf() + { + return quotient_and_pdf::create(U(1.0), pdf()); + } +}; +} + +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/shapes/aabb.hlsl b/include/nbl/builtin/hlsl/shapes/aabb.hlsl index 9c24eff0a5..7976acdb0d 100644 --- a/include/nbl/builtin/hlsl/shapes/aabb.hlsl +++ b/include/nbl/builtin/hlsl/shapes/aabb.hlsl @@ -4,8 +4,9 @@ #ifndef _NBL_BUILTIN_HLSL_SHAPES_AABB_INCLUDED_ #define _NBL_BUILTIN_HLSL_SHAPES_AABB_INCLUDED_ -#include - +#include "nbl/builtin/hlsl/concepts.hlsl" +#include "nbl/builtin/hlsl/limits.hlsl" +#include "nbl/builtin/hlsl/shapes/util.hlsl" namespace nbl { @@ -14,38 +15,84 @@ namespace hlsl namespace shapes { -struct AABB_t +template +struct AABB { + using point_t = vector; + + static AABB create() + { + AABB retval; + retval.minVx = promote(numeric_limits::max); + retval.maxVx = promote(numeric_limits::lowest); + return retval; + } + // - void addPoint(const float3 pt) + void addPoint(const point_t pt) { - minVx = min(pt, minVx); - maxVx = max(pt, maxVx); + minVx = min(pt,minVx); + maxVx = max(pt,maxVx); } // - float3 getExtent() + point_t getExtent() { return maxVx - minVx; } // - float getVolume() + Scalar getVolume() { - const float3 extent = AABB_t::getExtent(); + const point_t extent = getExtent(); return extent.x * extent.y * extent.z; } // returns the corner of the AABB which has the most positive dot product - float3 getFarthestPointInFront(const float3 plane) + point_t getFarthestPointInFront(const point_t planeNormal) + { + return hlsl::mix(maxVx,minVx,planeNormal < promote(0.f)); + } + + point_t minVx; + point_t maxVx; +}; + +namespace util +{ +namespace impl +{ +template +struct intersect_helper> +{ + using type = AABB; + + static inline type __call(NBL_CONST_REF_ARG(type) lhs, NBL_CONST_REF_ARG(type) rhs) { - return lerp(maxVx, minVx, plane(lhs.minVx,rhs.minVx); + retval.maxVx = min(lhs.maxVx,rhs.maxVx); + return retval; } +}; +template +struct union_helper> +{ + using type = AABB; - float3 minVx; - float3 maxVx; + static inline type __call(NBL_CONST_REF_ARG(type) lhs, NBL_CONST_REF_ARG(type) rhs) + { + type retval; + retval.minVx = min(lhs.minVx,rhs.minVx); + retval.maxVx = max(lhs.maxVx,rhs.maxVx); + return retval; + } }; +} +} -struct nbl_glsl_shapes_CompressedAABB_t +#if 0 // experimental +#include +struct CompressedAABB_t { // AABB_t decompress() @@ -59,6 +106,7 @@ struct nbl_glsl_shapes_CompressedAABB_t uint2 minVx18E7S3; uint2 maxVx18E7S3; }; +#endif } diff --git a/include/nbl/builtin/hlsl/shapes/beziers.hlsl b/include/nbl/builtin/hlsl/shapes/beziers.hlsl index 4a8acc4fd0..662ac22453 100644 --- a/include/nbl/builtin/hlsl/shapes/beziers.hlsl +++ b/include/nbl/builtin/hlsl/shapes/beziers.hlsl @@ -14,6 +14,7 @@ #include #include +#include namespace nbl { diff --git a/include/nbl/builtin/hlsl/shapes/line.hlsl b/include/nbl/builtin/hlsl/shapes/line.hlsl index db59d2ef9d..60a0718d64 100644 --- a/include/nbl/builtin/hlsl/shapes/line.hlsl +++ b/include/nbl/builtin/hlsl/shapes/line.hlsl @@ -13,72 +13,97 @@ namespace hlsl { namespace shapes { - template - struct Line - { - using scalar_t = float_t; - using float_t2 = vector; - using float_t3 = vector; - using float_t2x2 = matrix; + +template +struct Line +{ + using scalar_t = float_t; + using float_t2 = vector; + using float_t3 = vector; + using float_t2x2 = matrix; - float_t2 P0; - float_t2 P1; + float_t2 P0; + float_t2 P1; - struct ArcLengthCalculator + struct ArcLengthCalculator + { + float_t len; + + static ArcLengthCalculator construct(NBL_CONST_REF_ARG(Line) segment) { - float_t len; - - static ArcLengthCalculator construct(NBL_CONST_REF_ARG(Line) segment) - { - ArcLengthCalculator ret = { segment.getLength() }; - return ret; - } - - float_t calcArcLen(float_t t) - { - return len * t; - } - - float_t calcArcLenInverse(NBL_CONST_REF_ARG(Line) segment, float_t min, float_t max, float_t arcLen, float_t accuracyThreshold, float_t hint) - { - return arcLen / len; - } - }; - - static Line construct(NBL_CONST_REF_ARG(float_t2) P0, NBL_CONST_REF_ARG(float_t2) P1) - { - Line ret = { P0, P1 }; + ArcLengthCalculator ret = { segment.getLength() }; return ret; } - float_t2 evaluate(float_t t) NBL_CONST_MEMBER_FUNC + float_t calcArcLen(float_t t) { - return t * (P1 - P0) + P0; + return len * t; } - - float_t getLength() NBL_CONST_MEMBER_FUNC + + float_t calcArcLenInverse(NBL_CONST_REF_ARG(Line) segment, float_t min, float_t max, float_t arcLen, float_t accuracyThreshold, float_t hint) { - return length(P1 - P0); + return arcLen / len; } + }; - NBL_CONSTEXPR_STATIC_INLINE uint32_t MaxCandidates = 1u; - using Candidates = vector; + static Line construct(NBL_CONST_REF_ARG(float_t2) P0, NBL_CONST_REF_ARG(float_t2) P1) + { + Line ret = { P0, P1 }; + return ret; + } - Candidates getClosestCandidates(NBL_CONST_REF_ARG(float_t2) pos) NBL_CONST_MEMBER_FUNC - { - Candidates ret; - float_t2 p0p1 = P1 - P0; - float_t2 posp0 = pos - P0; - ret[0] = dot(posp0, p0p1) / dot(p0p1, p0p1); - return ret; - } + float_t2 evaluate(float_t t) NBL_CONST_MEMBER_FUNC + { + return t * (P1 - P0) + P0; + } + + float_t getLength() NBL_CONST_MEMBER_FUNC + { + return length(P1 - P0); + } + + NBL_CONSTEXPR_STATIC_INLINE uint32_t MaxCandidates = 1u; + using Candidates = vector; + + Candidates getClosestCandidates(NBL_CONST_REF_ARG(float_t2) pos) NBL_CONST_MEMBER_FUNC + { + Candidates ret; + float_t2 p0p1 = P1 - P0; + float_t2 posp0 = pos - P0; + ret[0] = dot(posp0, p0p1) / dot(p0p1, p0p1); + return ret; + } + + float_t2x2 getLocalCoordinateSpace(float_t t) NBL_CONST_MEMBER_FUNC + { + float_t2 d = normalize(P1 - P0); + return float_t2x2(d.x, d.y, -d.y, d.x); + } +}; + +namespace util +{ +template +static vector LineLineIntersection(NBL_CONST_REF_ARG(vector) P1, NBL_CONST_REF_ARG(vector) V1, NBL_CONST_REF_ARG(vector) P2, NBL_CONST_REF_ARG(vector) V2) +{ + typedef vector float_t2; + + float_t denominator = V1.y * V2.x - V1.x * V2.y; + vector diff = P1 - P2; + float_t numerator = dot(float_t2(V2.y, -V2.x), float_t2(diff.x, diff.y)); + + if (abs(denominator) < 1e-15 && abs(numerator) < 1e-15) + { + // are parallel and the same + return (P1 + P2) / 2.0; + } + + float_t t = numerator / denominator; + float_t2 intersectionPoint = P1 + t * V1; + return intersectionPoint; +} +} - float_t2x2 getLocalCoordinateSpace(float_t t) NBL_CONST_MEMBER_FUNC - { - float_t2 d = normalize(P1 - P0); - return float_t2x2(d.x, d.y, -d.y, d.x); - } - }; } } } diff --git a/include/nbl/builtin/hlsl/shapes/util.hlsl b/include/nbl/builtin/hlsl/shapes/util.hlsl index f3f45c87e0..a32ac7858b 100644 --- a/include/nbl/builtin/hlsl/shapes/util.hlsl +++ b/include/nbl/builtin/hlsl/shapes/util.hlsl @@ -16,26 +16,20 @@ namespace shapes namespace util { -template -static vector LineLineIntersection(NBL_CONST_REF_ARG(vector) P1, NBL_CONST_REF_ARG(vector) V1, NBL_CONST_REF_ARG(vector) P2, NBL_CONST_REF_ARG(vector) V2) +namespace impl { - typedef vector float_t2; - - float_t denominator = V1.y * V2.x - V1.x * V2.y; - vector diff = P1 - P2; - float_t numerator = dot(float_t2(V2.y, -V2.x), float_t2(diff.x, diff.y)); - - if (abs(denominator) < 1e-15 && abs(numerator) < 1e-15) - { - // are parallel and the same - return (P1 + P2) / 2.0; - } - - float_t t = numerator / denominator; - float_t2 intersectionPoint = P1 + t * V1; - return intersectionPoint; +template +struct intersect_helper; +template +struct union_helper; } +template +T intersect(NBL_CONST_REF_ARG(T) lhs, NBL_CONST_REF_ARG(T) rhs) {return impl::intersect_helper::__call(lhs,rhs);} +// union is a keyword in C++ +template +T union_(NBL_CONST_REF_ARG(T) lhs, NBL_CONST_REF_ARG(T) rhs) {return impl::union_helper::__call(lhs,rhs);} + } } } diff --git a/include/nbl/builtin/hlsl/spirv_intrinsics/core.hlsl b/include/nbl/builtin/hlsl/spirv_intrinsics/core.hlsl index 4885fc11f8..88941b8d6d 100644 --- a/include/nbl/builtin/hlsl/spirv_intrinsics/core.hlsl +++ b/include/nbl/builtin/hlsl/spirv_intrinsics/core.hlsl @@ -332,11 +332,11 @@ enable_if_t, Integral> bitCount(Integral mat); template [[vk::ext_instruction(spv::OpAll)]] -enable_if_t && is_same_v::scalar_type, bool>, BooleanVector> all(BooleanVector vec); +enable_if_t && is_same_v::scalar_type, bool>, bool> all(BooleanVector vec); template [[vk::ext_instruction(spv::OpAny)]] -enable_if_t&& is_same_v::scalar_type, bool>, BooleanVector> any(BooleanVector vec); +enable_if_t&& is_same_v::scalar_type, bool>, bool> any(BooleanVector vec); template) [[vk::ext_instruction(spv::OpIAddCarry)]] @@ -356,7 +356,7 @@ template && !is_matrix_v) conditional_t, vector::Dimension>, bool> FOrdEqual(T lhs, T rhs); -template && !is_matrix_v && is_same_v::scalar_type, bool>) +template && (is_scalar_v || (is_vector_v && is_vector_v && vector_traits::Dimension==vector_traits::Dimension)) && is_same_v::scalar_type, bool>) [[vk::ext_instruction(spv::OpSelect)]] T select(U a, T x, T y); diff --git a/include/nbl/builtin/hlsl/type_traits.hlsl b/include/nbl/builtin/hlsl/type_traits.hlsl index a6f9ad0655..2bad563829 100644 --- a/include/nbl/builtin/hlsl/type_traits.hlsl +++ b/include/nbl/builtin/hlsl/type_traits.hlsl @@ -403,27 +403,6 @@ struct is_compound : bool_constant::value> {}; template struct is_aggregate : is_compound {}; -template -struct rank : integral_constant { }; - -template -struct rank : integral_constant::value> { }; - -template -struct rank : integral_constant::value> { }; - -template -struct extent : integral_constant {}; - -template -struct extent : integral_constant {}; - -template -struct extent : integral_constant::value> {}; - -template -struct extent : integral_constant::value> {}; - template struct enable_if {}; @@ -663,7 +642,7 @@ template using conditional_t = typename conditional::type; -// Template Variables +// Template variables template NBL_CONSTEXPR bool is_same_v = is_same::value; template @@ -680,9 +659,6 @@ template NBL_CONSTEXPR uint64_t size_of_v = size_of::value; template NBL_CONSTEXPR uint32_t alignment_of_v = alignment_of::value; -template -NBL_CONSTEXPR uint64_t extent_v = extent::value; - // Overlapping definitions template @@ -718,6 +694,53 @@ template NBL_CONSTEXPR bool is_matrix_v = is_matrix::value; +#ifdef __HLSL_VERSION +template +struct rank : integral_constant, + uint64_t, + 2ull, + conditional_value< + is_vector_v, + uint64_t, + 1ull, + 0ull + >::value + >::value +> { }; + +template +struct rank : integral_constant::value> { }; + +template +struct rank : integral_constant::value> { }; + +template +struct extent : integral_constant {}; + +template +struct extent : integral_constant {}; + +template +struct extent : integral_constant::value> {}; + +template +struct extent : integral_constant::value> {}; + +template +struct extent, 0> : integral_constant {}; + +template +struct extent, I> : integral_constant::value> {}; +#endif + + +// Template Variables +template +NBL_CONSTEXPR uint64_t extent_v = extent::value; + + template::value> struct scalar_type { diff --git a/include/nbl/ext/MitsubaLoader/CMitsubaSerializedMetadata.h b/include/nbl/ext/MitsubaLoader/CMitsubaSerializedMetadata.h index 7be0bb89d6..a8082cafef 100644 --- a/include/nbl/ext/MitsubaLoader/CMitsubaSerializedMetadata.h +++ b/include/nbl/ext/MitsubaLoader/CMitsubaSerializedMetadata.h @@ -1,19 +1,15 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_C_MITSUBA_SERIALIZED_PIPELINE_METADATA_H_INCLUDED_ +#define _NBL_C_MITSUBA_SERIALIZED_PIPELINE_METADATA_H_INCLUDED_ -#ifndef __NBL_C_MITSUBA_SERIALIZED_PIPELINE_METADATA_H_INCLUDED__ -#define __NBL_C_MITSUBA_SERIALIZED_PIPELINE_METADATA_H_INCLUDED__ -#include "nbl/asset/ICPURenderpassIndependentPipeline.h" -#include "nbl/asset/ICPUMesh.h" +#include "nbl/asset/ICPUPolygonGeometry.h" #include "nbl/asset/metadata/IAssetMetadata.h" -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader + +namespace nbl::ext::MitsubaLoader { class CMitsubaSerializedMetadata final : public asset::IAssetMetadata @@ -82,7 +78,4 @@ class CMitsubaSerializedMetadata final : public asset::IAssetMetadata }; } -} -} - #endif \ No newline at end of file diff --git a/include/nbl/ext/MitsubaLoader/CSerializedLoader.h b/include/nbl/ext/MitsubaLoader/CSerializedLoader.h index 8bf540e2ef..4ded07d423 100644 --- a/include/nbl/ext/MitsubaLoader/CSerializedLoader.h +++ b/include/nbl/ext/MitsubaLoader/CSerializedLoader.h @@ -1,21 +1,20 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_EXT_MITSUBA_C_SERIALIZED_LOADER_H_INCLUDED_ +#define _NBL_EXT_MITSUBA_C_SERIALIZED_LOADER_H_INCLUDED_ -#ifndef __C_SERIALIZED_LOADER_H_INCLUDED__ -#define __C_SERIALIZED_LOADER_H_INCLUDED__ + +#include "nbl/system/declarations.h" #include "nbl/asset/asset.h" -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader + +namespace nbl::ext::MitsubaLoader { //! Meshloader capable of loading obj meshes. -class CSerializedLoader final : public asset::IRenderpassIndependentPipelineLoader +class CSerializedLoader final : public asset::IGeometryLoader { protected: //! Destructor @@ -23,7 +22,7 @@ class CSerializedLoader final : public asset::IRenderpassIndependentPipelineLoad public: //! Constructor - CSerializedLoader(asset::IAssetManager* _manager) : IRenderpassIndependentPipelineLoader(_manager) {} + CSerializedLoader(asset::IAssetManager* _manager) : IGeometryLoader() {} inline bool isALoadableFileFormat(system::IFile* _file, const system::logger_opt_ptr logger = nullptr) const override { @@ -42,8 +41,6 @@ class CSerializedLoader final : public asset::IRenderpassIndependentPipelineLoad return ext; } - inline uint64_t getSupportedAssetTypesBitfield() const override { return asset::IAsset::ET_MESH; } - //! creates/loads an animated mesh from the file. asset::SAssetBundle loadAsset(system::IFile* _file, const asset::IAssetLoader::SAssetLoadParams& _params, asset::IAssetLoader::IAssetLoaderOverride* _override = nullptr, uint32_t _hierarchyLevel = 0u) override; @@ -71,8 +68,4 @@ class CSerializedLoader final : public asset::IRenderpassIndependentPipelineLoad } -} -} - #endif - diff --git a/include/nbl/video/IGPUMesh.h b/include/nbl/video/IGPUMesh.h deleted file mode 100644 index 1ad2a74d9a..0000000000 --- a/include/nbl/video/IGPUMesh.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h -#ifndef _NBL_VIDEO_I_GPU_MESH_H_INCLUDED_ -#define _NBL_VIDEO_I_GPU_MESH_H_INCLUDED_ - -#if 0 // rewrite -#include "nbl/asset/IMesh.h" -#include "nbl/video/IGPUMeshBuffer.h" - -namespace nbl -{ -namespace video -{ - -class IGPUMesh final : public asset::IMesh -{ - using MeshBufferRefContainer = core::smart_refctd_dynamic_array>; - MeshBufferRefContainer m_meshBuffers; - - public: - IGPUMesh(uint32_t meshBufferCount) : m_meshBuffers(core::make_refctd_dynamic_array(meshBufferCount)) - { - } - - template - IGPUMesh(MeshBufferIterator begin, MeshBufferIterator end) : IGPUMesh(std::distance(begin,end)) - { - std::copy(begin,end,m_meshBuffers->begin()); - } - - inline auto getMeshBufferIterator() - { - return m_meshBuffers->data(); - } - - inline core::SRange getMeshBuffers() const override - { - auto begin = reinterpret_cast(m_meshBuffers->data()); - return core::SRange(begin,begin+m_meshBuffers->size()); - } - inline core::SRange getMeshBuffers() override - { - auto begin = reinterpret_cast(m_meshBuffers->data()); - return core::SRange(begin,begin+m_meshBuffers->size()); - } -}; - -} -} -#endif - -#endif \ No newline at end of file diff --git a/include/nbl/video/IGPUMeshBuffer.h b/include/nbl/video/IGPUMeshBuffer.h deleted file mode 100644 index 89f7aa13bf..0000000000 --- a/include/nbl/video/IGPUMeshBuffer.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h -#ifndef _NBL_VIDEO_I_GPU_MESH_BUFFER_H_INCLUDED_ -#define _NBL_VIDEO_I_GPU_MESH_BUFFER_H_INCLUDED_ - -#if 0 // rewrite -#include "nbl/asset/asset.h" - -#include - -#include "nbl/video/IGPUBuffer.h" -#include "nbl/video/IGPUDescriptorSet.h" - - -namespace nbl::video -{ - -class IGPUMeshBuffer final : public asset::IMeshBuffer -{ - public: - using base_t = asset::IMeshBuffer; - - using base_t::base_t; -}; - -} // end namespace nbl::video -#endif - -#endif - - diff --git a/include/nbl/video/IGPUPolygonGeometry.h b/include/nbl/video/IGPUPolygonGeometry.h new file mode 100644 index 0000000000..171f871d01 --- /dev/null +++ b/include/nbl/video/IGPUPolygonGeometry.h @@ -0,0 +1,66 @@ +// Copyright (C) 2018-2023 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_VIDEO_I_GPU_POLYGON_GEOMETRY_H_INCLUDED_ +#define _NBL_VIDEO_I_GPU_POLYGON_GEOMETRY_H_INCLUDED_ + + +#include "nbl/asset/IPolygonGeometry.h" + +#include "nbl/video/IGPUBuffer.h" + + +namespace nbl::video +{ + +// +class IGPUPolygonGeometry final : public asset::IPolygonGeometry +{ + using base_t = asset::IPolygonGeometry; + + public: + using SDataView = base_t::SDataView; + struct SCreationParams + { + SDataView positionView = {}; + SDataView jointOBBView = {}; + SDataView indexView = {}; + const IIndexingCallback* indexing = nullptr; + SDataView normalView = {}; + std::span jointWeightViews = {}; + std::span auxAttributeViews = {}; + uint32_t jointCount = 0; + }; + static inline core::smart_refctd_ptr create(SCreationParams&& params) + { + auto retval = core::smart_refctd_ptr(new IGPUPolygonGeometry(),core::dont_grab); + retval->m_positionView = params.positionView; + if (params.jointCount) + retval->m_jointOBBView = params.jointOBBView; + retval->m_indexView = params.indexView; + retval->m_indexing = params.indexing; + if (params.jointCount) + retval->m_jointWeightViews.insert(retval->m_jointWeightViews.begin(),params.jointWeightViews.begin(),params.jointWeightViews.end()); + retval->m_normalView = params.normalView; + retval->m_auxAttributeViews.insert(retval->m_auxAttributeViews.begin(),params.auxAttributeViews.begin(),params.auxAttributeViews.end()); + retval->m_jointCount = params.jointCount; + if (!retval->valid()) + return nullptr; + return retval; + } + + // passthrough +#if 0 + inline const SDataView& getNormalView() const {return base_t::getNormalView();} + inline const core::vector& getJointWeightViews() const {return base_t::getJointWeightViews();} + inline const core::vector& getAuxAttributeViews() const {return base_t::getAuxAttributeViews();} +#endif + + private: + inline IGPUPolygonGeometry() = default; + inline ~IGPUPolygonGeometry() = default; +}; + +} + +#endif \ No newline at end of file diff --git a/include/nbl/video/ILogicalDevice.h b/include/nbl/video/ILogicalDevice.h index def3ee0979..6298afeb27 100644 --- a/include/nbl/video/ILogicalDevice.h +++ b/include/nbl/video/ILogicalDevice.h @@ -366,6 +366,11 @@ class NBL_API2 ILogicalDevice : public core::IReferenceCounted, public IDeviceMe // Create an ImageView that can actually be used by shaders (@see ICPUImageView) inline core::smart_refctd_ptr createImageView(IGPUImageView::SCreationParams&& params) { + if (!params.image) + { + NBL_LOG_ERROR("The image is null"); + return nullptr; + } if (!params.image->wasCreatedBy(this)) { NBL_LOG_ERROR("The image was not created by this device"); diff --git a/include/nbl/video/asset_traits.h b/include/nbl/video/asset_traits.h index 5b085b2d3b..faf5322798 100644 --- a/include/nbl/video/asset_traits.h +++ b/include/nbl/video/asset_traits.h @@ -19,6 +19,8 @@ #include "nbl/video/IGPUImageView.h" #include "nbl/asset/ICPUAccelerationStructure.h" #include "nbl/video/IGPUAccelerationStructure.h" +#include "nbl/asset/ICPUPolygonGeometry.h" +#include "nbl/video/IGPUPolygonGeometry.h" namespace nbl::video @@ -228,6 +230,20 @@ struct asset_traits }; +template<> +struct asset_traits +{ + // the asset type + using asset_t = asset::ICPUPolygonGeometry; + // depends on `ICPUBuffer` + constexpr static inline bool HasChildren = true; + // the video type + using video_t = IGPUPolygonGeometry; + // lookup type + using lookup_t = const video_t*; +}; + + /* TODO template<> struct asset_traits; diff --git a/include/nbl/video/declarations.h b/include/nbl/video/declarations.h index 2fdfe28e3c..37f2f864bf 100644 --- a/include/nbl/video/declarations.h +++ b/include/nbl/video/declarations.h @@ -41,7 +41,6 @@ #include "nbl/video/utilities/CAssetConverter.h" //VT -//#include "nbl/video/CGPUMeshPackerV2.h" //#include "nbl/video/IGPUVirtualTexture.h" diff --git a/include/nbl/video/utilities/CAssetConverter.h b/include/nbl/video/utilities/CAssetConverter.h index 935b79b1e5..3f0225a78e 100644 --- a/include/nbl/video/utilities/CAssetConverter.h +++ b/include/nbl/video/utilities/CAssetConverter.h @@ -50,8 +50,9 @@ class CAssetConverter : public core::IReferenceCounted asset::ICPUComputePipeline, asset::ICPURenderpass, asset::ICPUGraphicsPipeline, - asset::ICPUDescriptorSet + asset::ICPUDescriptorSet, //asset::ICPUFramebuffer doesn't exist yet XD + asset::ICPUPolygonGeometry >; struct SCreationParams @@ -92,11 +93,11 @@ class CAssetConverter : public core::IReferenceCounted { #define PATCH_IMPL_BOILERPLATE(ASSET_TYPE) using this_t = patch_impl_t; \ public: \ - inline patch_impl_t() = default; \ - inline patch_impl_t(const this_t& other) = default; \ - inline patch_impl_t(this_t&& other) = default; \ - inline this_t& operator=(const this_t& other) = default; \ - inline this_t& operator=(this_t&& other) = default; \ + constexpr inline patch_impl_t() = default; \ + constexpr inline patch_impl_t(const this_t& other) = default; \ + constexpr inline patch_impl_t(this_t&& other) = default; \ + constexpr inline this_t& operator=(const this_t& other) = default; \ + constexpr inline this_t& operator=(this_t&& other) = default; \ patch_impl_t(const ASSET_TYPE* asset); \ bool valid(const ILogicalDevice* device) @@ -334,11 +335,11 @@ class CAssetConverter : public core::IReferenceCounted using this_t = patch_impl_t; public: - inline patch_impl_t() = default; - inline patch_impl_t(const this_t& other) = default; - inline patch_impl_t(this_t&& other) = default; - inline this_t& operator=(const this_t& other) = default; - inline this_t& operator=(this_t&& other) = default; + constexpr inline patch_impl_t() = default; + constexpr inline patch_impl_t(const this_t& other) = default; + constexpr inline patch_impl_t(this_t&& other) = default; + constexpr inline this_t& operator=(const this_t& other) = default; + constexpr inline this_t& operator=(this_t&& other) = default; using usage_flags_t = IGPUImage::E_USAGE_FLAGS; // slightly weird constructor because it deduces the metadata from subusages, so need the subusages right away, not patched later @@ -414,6 +415,29 @@ class CAssetConverter : public core::IReferenceCounted bool invalid = true; }; + template<> + struct NBL_API2 patch_impl_t + { + public: + PATCH_IMPL_BOILERPLATE(asset::ICPUPolygonGeometry); + + using usage_flags_t = IGPUBuffer::E_USAGE_FLAGS; + // assume programmable pulling for all attributes + core::bitflag positionBufferUsages = usage_flags_t::EUF_SHADER_DEVICE_ADDRESS_BIT; + // assume nothing + core::bitflag indexBufferUsages = usage_flags_t::EUF_NONE; + core::bitflag otherBufferUsages = usage_flags_t::EUF_SHADER_DEVICE_ADDRESS_BIT; + + protected: + inline std::pair combine(const this_t& other) const + { + this_t retval = *this; + retval.indexBufferUsages |= other.indexBufferUsages; + retval.positionBufferUsages |= other.positionBufferUsages; + retval.otherBufferUsages |= other.otherBufferUsages; + return {true,retval}; + } + }; #undef PATCH_IMPL_BOILERPLATE // The default specialization provides simple equality operations and hash operations, this will work as long as your patch_impl_t doesn't: // - use a container like `core::vector`, etc. @@ -426,12 +450,12 @@ class CAssetConverter : public core::IReferenceCounted // forwarding using base_t::base_t; - inline patch_t(const this_t& other) : base_t(other) {} - inline patch_t(this_t&& other) : base_t(std::move(other)) {} - inline patch_t(base_t&& other) : base_t(std::move(other)) {} + constexpr inline patch_t(const this_t& other) : base_t(other) {} + constexpr inline patch_t(this_t&& other) : base_t(std::move(other)) {} + constexpr inline patch_t(base_t&& other) : base_t(std::move(other)) {} - inline this_t& operator=(const this_t& other) = default; - inline this_t& operator=(this_t&& other) = default; + constexpr inline this_t& operator=(const this_t& other) = default; + constexpr inline this_t& operator=(this_t&& other) = default; // The assumption is we'll only ever be combining valid patches together. // Returns: whether the combine op was a success, DOESN'T MEAN the result is VALID! @@ -537,6 +561,7 @@ class CAssetConverter : public core::IReferenceCounted virtual const patch_t* operator()(const lookup_t&) const = 0; virtual const patch_t* operator()(const lookup_t&) const = 0; virtual const patch_t* operator()(const lookup_t&) const = 0; + virtual const patch_t* operator()(const lookup_t&) const = 0; // certain items are not patchable, so there's no `patch_t` with non zero size inline const patch_t* operator()(const lookup_t& unpatchable) const @@ -555,7 +580,7 @@ class CAssetConverter : public core::IReferenceCounted { return unpatchable.patch; } - + // while other things are top level assets in the graph and `operator()` would never be called on their patch }; // `cacheMistrustLevel` is how deep from `asset` do we start trusting the cache to contain correct non stale hashes @@ -668,6 +693,7 @@ class CAssetConverter : public core::IReferenceCounted bool operator()(lookup_t); bool operator()(lookup_t); bool operator()(lookup_t); + bool operator()(lookup_t); }; // diff --git a/include/nbl/video/utilities/CGPUMeshPackerV2.h b/include/nbl/video/utilities/CGPUMeshPackerV2.h deleted file mode 100644 index c6e900e824..0000000000 --- a/include/nbl/video/utilities/CGPUMeshPackerV2.h +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_C_GPU_MESH_PACKER_V2_H_INCLUDED__ -#define __NBL_ASSET_C_GPU_MESH_PACKER_V2_H_INCLUDED__ - -#include -#include -#include -#include -#include - -using namespace nbl::video; - -namespace nbl -{ -namespace video -{ - -#if 0 // REWRITE -template -class CGPUMeshPackerV2 final : public asset::IMeshPackerV2 -{ - using base_t = asset::IMeshPackerV2; - using Triangle = typename base_t::Triangle; - using TriangleBatches = typename base_t::TriangleBatches; - - public: - using AllocationParams = typename base_t::AllocationParamsCommon; - using PackerDataStore = typename base_t::PackerDataStore; - using ReservedAllocationMeshBuffers = typename base_t::ReservedAllocationMeshBuffers; - using AttribAllocParams = typename base_t::AttribAllocParams; - - public: - CGPUMeshPackerV2(ILogicalDevice* driver, const AllocationParams& allocParams, const asset::IMeshPackerV2Base::SupportedFormatsContainer& formats, uint16_t minTriangleCountPerMDIData = 256u, uint16_t maxTriangleCountPerMDIData = 1024u) - : base_t(allocParams, formats, minTriangleCountPerMDIData, maxTriangleCountPerMDIData), m_driver(driver) - { - m_utilities = core::make_smart_refctd_ptr(core::smart_refctd_ptr(driver)); - } - - // TODO: protect against empty cpuMP (no allocations and then shrinked) - CGPUMeshPackerV2(ILogicalDevice* driver, IQueue* queue, const asset::CCPUMeshPackerV2* cpuMP) - : base_t(cpuMP), m_driver(driver) - { - // TODO: protect against unitiliazed storage of cpuMP - const auto& cpuMDIBuff = cpuMP->getPackerDataStore().MDIDataBuffer; - const auto& cpuIdxBuff = cpuMP->getPackerDataStore().indexBuffer; - const auto& cpuVtxBuff = cpuMP->getPackerDataStore().vertexBuffer; - - m_utilities = core::make_smart_refctd_ptr(core::smart_refctd_ptr(driver)); - - // TODO: call this->instantiateDataStorage() here and then copy CPU data to the initialized storage - base_t::m_packerDataStore.MDIDataBuffer = m_utilities->createFilledDeviceLocalBufferOnDedMem(queue, cpuMDIBuff->getSize(),cpuMDIBuff->getPointer()); - base_t::m_packerDataStore.indexBuffer = m_utilities->createFilledDeviceLocalBufferOnDedMem(queue, cpuIdxBuff->getSize(),cpuIdxBuff->getPointer()); - base_t::m_packerDataStore.vertexBuffer = m_utilities->createFilledDeviceLocalBufferOnDedMem(queue, cpuVtxBuff->getSize(),cpuVtxBuff->getPointer()); - } - - void instantiateDataStorage(); - - template - bool commit(typename base_t::PackedMeshBufferData* pmbdOut, ReservedAllocationMeshBuffers* rambIn, core::aabbox3df* aabbs, const MeshBufferIterator mbBegin, const MeshBufferIterator mbEnd); - - inline std::pair getDescriptorSetWritesForUTB( - IGPUDescriptorSet::SWriteDescriptorSet* outWrites, IGPUDescriptorSet::SDescriptorInfo* outInfo, IGPUDescriptorSet* dstSet, - const typename base_t::DSLayoutParamsUTB& params = {} - ) const - { - auto createBufferView = [&](core::smart_refctd_ptr&& buff, asset::E_FORMAT format) -> core::smart_refctd_ptr - { - return m_driver->createBufferView(buff.get(),format); - }; - return base_t::getDescriptorSetWritesForUTB(outWrites,outInfo,dstSet,createBufferView,params); - } - private: - core::smart_refctd_ptr m_utilities; - ILogicalDevice* m_driver; - -}; - -template -void CGPUMeshPackerV2::instantiateDataStorage() -{ - // Update to NewBufferAPI when porting - // auto createAndAllocateBuffer = [&](size_t size) -> auto - // { - // video::IGPUBuffer::SCreationParams creationParams = {}; - // creationParams.size = size; - // creationParams.usage = asset::IBuffer::EUF_STORAGE_BUFFER_BIT; ??? - // auto buffer = params.device->createBuffer(creationParams); - // auto mreqs = buffer->getMemoryReqs(); - // mreqs.memoryTypeBits &= params.device->getPhysicalDevice()->getDeviceLocalMemoryTypeBits(); - // auto gpubufMem = params.device->allocate(mreqs, buffer.get()); - // return buffer; - // }; - - const uint32_t MDIDataBuffByteSize = base_t::m_MDIDataAlctr.get_total_size() * sizeof(MDIStructType); - const uint32_t idxBuffByteSize = base_t::m_idxBuffAlctr.get_total_size() * sizeof(uint16_t); - const uint32_t vtxBuffByteSize = base_t::m_vtxBuffAlctr.get_total_size(); - - base_t::m_packerDataStore.MDIDataBuffer = m_driver->createDeviceLocalGPUBufferOnDedMem(MDIDataBuffByteSize); - base_t::m_packerDataStore.indexBuffer = m_driver->createDeviceLocalGPUBufferOnDedMem(idxBuffByteSize); - base_t::m_packerDataStore.vertexBuffer = m_driver->createDeviceLocalGPUBufferOnDedMem(vtxBuffByteSize); -} - -template -template -bool CGPUMeshPackerV2::commit(typename base_t::PackedMeshBufferData* pmbdOut, ReservedAllocationMeshBuffers* rambIn, core::aabbox3df* aabbs, const MeshBufferIterator mbBegin, const MeshBufferIterator mbEnd) -{ - assert(0); - return false; -} -#endif -} -} - -#endif \ No newline at end of file diff --git a/src/nbl/CMakeLists.txt b/src/nbl/CMakeLists.txt index 2dddc74f77..adf320a5ee 100755 --- a/src/nbl/CMakeLists.txt +++ b/src/nbl/CMakeLists.txt @@ -53,9 +53,9 @@ option(_NBL_COMPILE_WITH_MTL_LOADER_ "Compile with MTL Loader" OFF) #default off option(_NBL_COMPILE_WITH_OBJ_LOADER_ "Compile with OBJ Loader" OFF) #default off until Material Compiler 2 #option(_NBL_COMPILE_WITH_OBJ_WRITER_ "Compile with OBJ Writer" ON) uncomment when writer exists option(_NBL_COMPILE_WITH_STL_LOADER_ "Compile with STL Loader" OFF) #default off until Material Compiler 2 -option(_NBL_COMPILE_WITH_STL_WRITER_ "Compile with STL Writer" ON) +option(_NBL_COMPILE_WITH_STL_WRITER_ "Compile with STL Writer" OFF) option(_NBL_COMPILE_WITH_PLY_LOADER_ "Compile with PLY Loader" OFF) #default off until Material Compiler 2 -option(_NBL_COMPILE_WITH_PLY_WRITER_ "Compile with PLY Writer" ON) +option(_NBL_COMPILE_WITH_PLY_WRITER_ "Compile with PLY Writer" OFF) option(_NBL_COMPILE_WITH_JPG_LOADER_ "Compile with JPG Loader" ON) option(_NBL_COMPILE_WITH_JPG_WRITER_ "Compile with JPG Writer" ON) option(_NBL_COMPILE_WITH_PNG_LOADER_ "Compile with PNG Loader" ON) @@ -156,9 +156,9 @@ set(NBL_ASSET_SOURCES ${NBL_ROOT_PATH}/src/nbl/asset/IAssetManager.cpp ${NBL_ROOT_PATH}/src/nbl/asset/ICPUDescriptorSet.cpp ${NBL_ROOT_PATH}/src/nbl/asset/ICPUImage.cpp + ${NBL_ROOT_PATH}/src/nbl/asset/ICPUPolygonGeometry.cpp ${NBL_ROOT_PATH}/src/nbl/asset/interchange/IAssetWriter.cpp ${NBL_ROOT_PATH}/src/nbl/asset/interchange/IAssetLoader.cpp - ${NBL_ROOT_PATH}/src/nbl/asset/interchange/IRenderpassIndependentPipelineLoader.cpp # Shaders ${NBL_ROOT_PATH}/src/nbl/asset/utils/ISPIRVOptimizer.cpp @@ -179,8 +179,8 @@ set(NBL_ASSET_SOURCES ${NBL_ROOT_PATH}/src/nbl/asset/utils/CForsythVertexCacheOptimizer.cpp ${NBL_ROOT_PATH}/src/nbl/asset/utils/CSmoothNormalGenerator.cpp ${NBL_ROOT_PATH}/src/nbl/asset/utils/CGeometryCreator.cpp - ${NBL_ROOT_PATH}/src/nbl/asset/utils/CMeshManipulator.cpp - ${NBL_ROOT_PATH}/src/nbl/asset/utils/COverdrawMeshOptimizer.cpp + ${NBL_ROOT_PATH}/src/nbl/asset/utils/CPolygonGeometryManipulator.cpp + ${NBL_ROOT_PATH}/src/nbl/asset/utils/COverdrawPolygonGeometryOptimizer.cpp ${NBL_ROOT_PATH}/src/nbl/asset/utils/CSmoothNormalGenerator.cpp # Mesh loaders @@ -350,21 +350,18 @@ if(NBL_CPACK_NO_BUILD_DIRECTORY_MODULES) target_compile_definitions(Nabla PUBLIC NBL_CPACK_NO_BUILD_DIRECTORY_MODULES) endif() -if(NBL_COMPILER_DYNAMIC_RUNTIME) - set_property(TARGET Nabla PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") -else() - set_property(TARGET Nabla PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") -endif() - -target_compile_definitions(Nabla PRIVATE __NBL_BUILDING_NABLA__) - -target_link_options(Nabla INTERFACE # proxy to downstream targets - $<$: - $<$:/DELAYLOAD:$> - /DELAYLOAD:dxcompiler.dll - > +target_compile_definitions(Nabla + PUBLIC _DXC_DLL_="${DXC_DLL}" + PRIVATE __NBL_BUILDING_NABLA__ ) +if(CMAKE_CXX_COMPILER_FRONTEND_VARIANT MATCHES MSVC) + target_link_options(Nabla + INTERFACE /DELAYLOAD:$ + PRIVATE /DELAYLOAD:dxcompiler.dll + ) +endif() + if (ANDROID) add_library(android_native_app_glue STATIC ${ANDROID_NDK_ROOT_PATH}/sources/android/native_app_glue/android_native_app_glue.c @@ -379,9 +376,12 @@ if (ANDROID) ) endif() +set(NBL_ASSEMBLY_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/$/devshgraphicsprogramming.nabla") if(NOT NBL_STATIC_BUILD) - set(NBL_ASSEMBLY_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/$/devshgraphicsprogramming.nabla" CACHE INTERNAL "" FORCE) set_target_properties(Nabla PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${NBL_ASSEMBLY_DIRECTORY}) + target_compile_definitions(Nabla PUBLIC + _NABLA_DLL_NAME_="$>";_NABLA_OUTPUT_DIR_="${NBL_ASSEMBLY_DIRECTORY}" + ) endif() ## Set up 3rdparty deps @@ -591,10 +591,10 @@ endif() # Include dirs for self target_include_directories(Nabla PUBLIC - ${CMAKE_CURRENT_BINARY_DIR}/include - ${NBL_ROOT_PATH}/include + "${CMAKE_CURRENT_BINARY_DIR}/include" + "${NBL_ROOT_PATH}/include" ${COMMON_INCLUDE_DIRS} - ${THIRD_PARTY_SOURCE_DIR} + "${THIRD_PARTY_SOURCE_DIR}" #those also goes as PUBLIC because of examples "$<$:${NABLA_CONF_DIR_DEBUG}>" "$<$:${NABLA_CONF_DIR_RELEASE}>" @@ -698,9 +698,13 @@ if (MSVC) target_compile_options(Nabla PUBLIC /bigobj) endif() -#precompiled headers if(NBL_PCH) - target_precompile_headers(Nabla PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/pch.h") + target_precompile_headers(Nabla + # private as nothing from source directory should ever leak to downstream targets! + # NOTE: currently our whole public and private interface is broken + # and private headers leak to public includes + PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/pch.h" + ) endif() # extensions @@ -774,3 +778,5 @@ else() endif() nbl_install_program_spec("${DXC_DLL}" "nbl/3rdparty/dxc") + +NBL_ADJUST_FOLDERS(src) \ No newline at end of file diff --git a/src/nbl/asset/IAssetManager.cpp b/src/nbl/asset/IAssetManager.cpp index 26b029585b..10dfeb33be 100644 --- a/src/nbl/asset/IAssetManager.cpp +++ b/src/nbl/asset/IAssetManager.cpp @@ -85,7 +85,6 @@ #include "nbl/asset/interchange/CBufferLoaderBIN.h" #include "nbl/asset/utils/CGeometryCreator.h" -#include "nbl/asset/utils/CMeshManipulator.h" using namespace nbl; @@ -115,22 +114,10 @@ std::function nbl::asset::makeAssetDisposeFunc(const IAsset void IAssetManager::initializeMeshTools() { - m_meshManipulator = core::make_smart_refctd_ptr(); - m_geometryCreator = core::make_smart_refctd_ptr(m_meshManipulator.get()); if (!m_compilerSet) m_compilerSet = core::make_smart_refctd_ptr(core::smart_refctd_ptr(m_system)); } -const IGeometryCreator* IAssetManager::getGeometryCreator() const -{ - return m_geometryCreator.get(); -} - -IMeshManipulator* IAssetManager::getMeshManipulator() -{ - return m_meshManipulator.get(); -} - void IAssetManager::addLoadersAndWriters() { #ifdef _NBL_COMPILE_WITH_STL_LOADER_ @@ -201,8 +188,6 @@ void IAssetManager::addLoadersAndWriters() SAssetBundle IAssetManager::getAssetInHierarchy_impl(system::IFile* _file, const std::string& _supposedFilename, const IAssetLoader::SAssetLoadParams& _params, uint32_t _hierarchyLevel, IAssetLoader::IAssetLoaderOverride* _override) { IAssetLoader::SAssetLoadParams params(_params); - if (params.meshManipulatorOverride == nullptr) - params.meshManipulatorOverride = m_meshManipulator.get(); IAssetLoader::SAssetLoadContext ctx{params,_file}; @@ -398,24 +383,6 @@ void IAssetManager::insertBuiltinAssets() //it's intentionally added to cache later, see comments below, dont touch this order of insertions } - //desc sets - { - auto ds1 = core::make_smart_refctd_ptr(core::smart_refctd_ptr(defaultDs1Layout.get())); - { - constexpr size_t UBO_SZ = sizeof(asset::SBasicViewParameters); - auto ubo = asset::ICPUBuffer::create({ UBO_SZ }); - //for filling this UBO with actual data, one can use asset::SBasicViewParameters struct defined in nbl/asset/asset_utils.h - asset::fillBufferWithDeadBeef(ubo.get()); - - auto descriptorInfos = ds1->getDescriptorInfos(ICPUDescriptorSetLayout::CBindingRedirect::binding_number_t(0), IDescriptor::E_TYPE::ET_UNIFORM_BUFFER); - descriptorInfos.begin()[0].desc = std::move(ubo); - descriptorInfos.begin()[0].info.buffer.offset = 0ull; - descriptorInfos.begin()[0].info.buffer.size = UBO_SZ; - } - addBuiltInToCaches(ds1, "nbl/builtin/descriptor_set/basic_view_parameters"); - addBuiltInToCaches(defaultDs1Layout, "nbl/builtin/descriptor_set_layout/basic_view_parameters"); - } - // pipeline layout core::smart_refctd_ptr pipelineLayout; { diff --git a/src/nbl/asset/ICPUPolygonGeometry.cpp b/src/nbl/asset/ICPUPolygonGeometry.cpp new file mode 100644 index 0000000000..74970f805b --- /dev/null +++ b/src/nbl/asset/ICPUPolygonGeometry.cpp @@ -0,0 +1,131 @@ +#include "nbl/asset/ICPUPolygonGeometry.h" + +#include + +using namespace nbl; +using namespace asset; + + +template requires (Order>0) +class CListIndexingCB final : public IPolygonGeometryBase::IIndexingCallback +{ + template + static void operator_impl(SContext& ctx) + { + auto indexOfIndex = ctx.beginPrimitive*3; + for (const auto end=ctx.endPrimitive*3; indexOfIndex!=end; indexOfIndex+=3) + ctx.streamOut(indexOfIndex,std::ranges::iota_view{0,int(Order)}); + } + + public: + uint8_t degree_impl() const override {return Order;} + uint8_t rate_impl() const override {return Order;} + void operator()(SContext& ctx) const override {operator_impl(ctx);} + void operator()(SContext& ctx) const override {operator_impl(ctx);} + void operator()(SContext& ctx) const override {operator_impl(ctx);} + + E_PRIMITIVE_TOPOLOGY knownTopology() const override + { + switch (Order) + { + case 1: + return EPT_POINT_LIST; + case 2: + return EPT_LINE_LIST; + case 3: + return EPT_TRIANGLE_LIST; + default: + return EPT_PATCH_LIST; + } + } +}; +auto IPolygonGeometryBase::PointList() -> IIndexingCallback* +{ + static CListIndexingCB<1> singleton; + return &singleton; +} +auto IPolygonGeometryBase::LineList() -> IIndexingCallback* +{ + static CListIndexingCB<2> singleton; + return &singleton; +} +auto IPolygonGeometryBase::TriangleList() -> IIndexingCallback* +{ + static CListIndexingCB<3> singleton; + return &singleton; +} +auto IPolygonGeometryBase::QuadList() -> IIndexingCallback* +{ + static CListIndexingCB<4> singleton; + return &singleton; +} + +class CTriangleStripIndexingCB final : public IPolygonGeometryBase::IIndexingCallback +{ + template + static void operator_impl(SContext& ctx) + { + uint64_t indexOfIndex; + if (ctx.beginPrimitive==0) + { + ctx.streamOut(0,std::ranges::iota_view{0,3}); + indexOfIndex = 3; + } + else + indexOfIndex = ctx.beginPrimitive+2; + const int32_t perm[] = {-1,-2,0}; + for (const auto end=ctx.endPrimitive+2; indexOfIndex!=end; indexOfIndex++) + ctx.streamOut(indexOfIndex,perm); + } + + public: + inline uint8_t degree_impl() const override { return 3; } + inline uint8_t rate_impl() const override { return 1; } + void operator()(SContext& ctx) const override { operator_impl(ctx); } + void operator()(SContext& ctx) const override { operator_impl(ctx); } + void operator()(SContext& ctx) const override { operator_impl(ctx); } + + E_PRIMITIVE_TOPOLOGY knownTopology() const override {return EPT_TRIANGLE_STRIP;} +}; +auto IPolygonGeometryBase::TriangleStrip() -> IIndexingCallback* +{ + static CTriangleStripIndexingCB singleton; + return &singleton; +} + +class CTriangleFanIndexingCB final : public IPolygonGeometryBase::IIndexingCallback +{ + template + static void operator_impl(SContext& ctx) + { + uint64_t indexOfIndex; + if (ctx.beginPrimitive==0) + { + ctx.streamOut(0,std::ranges::iota_view{0,3}); + indexOfIndex = 3; + } + else + indexOfIndex = ctx.beginPrimitive+2; + int32_t perm[] = {0x7eadbeefu,-1,0}; + for (const auto end=ctx.endPrimitive+2; indexOfIndex!=end; indexOfIndex++) + { + // first index is always global 0 + perm[0] = -indexOfIndex; + ctx.streamOut(indexOfIndex,perm); + } + } + + public: + inline uint8_t degree_impl() const override {return 3;} + inline uint8_t rate_impl() const override {return 1;} + void operator()(SContext& ctx) const override { operator_impl(ctx); } + void operator()(SContext& ctx) const override { operator_impl(ctx); } + void operator()(SContext& ctx) const override { operator_impl(ctx); } + + E_PRIMITIVE_TOPOLOGY knownTopology() const override {return EPT_TRIANGLE_FAN;} +}; +auto IPolygonGeometryBase::TriangleFan() -> IIndexingCallback* +{ + static CTriangleFanIndexingCB singleton; + return &singleton; +} \ No newline at end of file diff --git a/src/nbl/asset/interchange/CBufferLoaderBIN.h b/src/nbl/asset/interchange/CBufferLoaderBIN.h index 2d0ea5f765..b47866ff3c 100644 --- a/src/nbl/asset/interchange/CBufferLoaderBIN.h +++ b/src/nbl/asset/interchange/CBufferLoaderBIN.h @@ -1,16 +1,13 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_C_BUFFER_LOADER_H_INCLUDED__ -#define __NBL_ASSET_C_BUFFER_LOADER_H_INCLUDED__ +#ifndef _NBL_ASSET_C_BUFFER_LOADER_H_INCLUDED_ +#define _NBL_ASSET_C_BUFFER_LOADER_H_INCLUDED_ #include "nbl/asset/interchange/IAssetLoader.h" -#include "nbl/asset/ICPUMeshBuffer.h" +#include "nbl/asset/ICPUBuffer.h" -namespace nbl -{ -namespace asset +namespace nbl::asset { //! Binaryloader capable of loading source code in binary format @@ -43,6 +40,4 @@ class CBufferLoaderBIN final : public asset::IAssetLoader }; } -} - #endif diff --git a/src/nbl/asset/interchange/CGraphicsPipelineLoaderMTL.cpp b/src/nbl/asset/interchange/CGraphicsPipelineLoaderMTL.cpp index 8d6f575ae5..d4b9a3e394 100644 --- a/src/nbl/asset/interchange/CGraphicsPipelineLoaderMTL.cpp +++ b/src/nbl/asset/interchange/CGraphicsPipelineLoaderMTL.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h @@ -15,17 +15,11 @@ #include "nbl/builtin/MTLdefaults.h" - using namespace nbl; using namespace asset; -#define VERT_SHADER_NO_UV_CACHE_KEY "nbl/builtin/shader/loader/mtl/vertex_no_uv.vert" -#define VERT_SHADER_UV_CACHE_KEY "nbl/builtin/shader/loader/mtl/vertex_uv.vert" -#define FRAG_SHADER_NO_UV_CACHE_KEY "nbl/builtin/shader/loader/mtl/fragment_no_uv.frag" -#define FRAG_SHADER_UV_CACHE_KEY "nbl/builtin/shader/loader/mtl/fragment_uv.frag" -CGraphicsPipelineLoaderMTL::CGraphicsPipelineLoaderMTL(IAssetManager* _am, core::smart_refctd_ptr&& sys) : - IRenderpassIndependentPipelineLoader(_am), m_system(std::move(sys)) +CGraphicsPipelineLoaderMTL::CGraphicsPipelineLoaderMTL(IAssetManager* _am, core::smart_refctd_ptr&& sys) : m_system(std::move(sys)) { #if 0 // Remove IRenderpassIndependentPipelines and use MC for Mesh Loaders //create vertex shaders and insert them into cache @@ -70,6 +64,7 @@ CGraphicsPipelineLoaderMTL::CGraphicsPipelineLoaderMTL(IAssetManager* _am, core: #endif } +#if 0 void CGraphicsPipelineLoaderMTL::initialize() { IRenderpassIndependentPipelineLoader::initialize(); @@ -123,6 +118,7 @@ void CGraphicsPipelineLoaderMTL::initialize() insertBuiltinAssetIntoCache(m_assetMgr, bundle, "nbl/builtin/renderpass_independent_pipeline/loader/mtl/missing_material_pipeline"); } +#endif bool CGraphicsPipelineLoaderMTL::isALoadableFileFormat(system::IFile* _file, const system::logger_opt_ptr logger) const { @@ -138,6 +134,8 @@ bool CGraphicsPipelineLoaderMTL::isALoadableFileFormat(system::IFile* _file, con SAssetBundle CGraphicsPipelineLoaderMTL::loadAsset(system::IFile* _file, const IAssetLoader::SAssetLoadParams& _params, IAssetLoader::IAssetLoaderOverride* _override, uint32_t _hierarchyLevel) { + return {}; +#if 0 // Remove IRenderpassIndependentPipelines and use MC for Mesh Loaders SContext ctx( asset::IAssetLoader::SAssetLoadContext{ _params, @@ -196,12 +194,12 @@ SAssetBundle CGraphicsPipelineLoaderMTL::loadAsset(system::IFile* _file, const I if (materials.empty()) return SAssetBundle(nullptr, {}); return SAssetBundle(std::move(meta),std::move(retval)); +#endif } +#if 0 // Remove IRenderpassIndependentPipelines and use MC for Mesh Loaders core::smart_refctd_ptr CGraphicsPipelineLoaderMTL::makePipelineFromMtl(SContext& _ctx, const SMtl& _mtl, bool hasUV) { - return nullptr; -#if 0 // Remove IRenderpassIndependentPipelines and use MC for Mesh Loaders SBlendParams blendParams; std::string cacheKey("nbl/builtin/renderpass_independent_pipeline/loader/mtl/"); @@ -323,8 +321,8 @@ core::smart_refctd_ptr CGraphicsPipelineLoade ppln = core::make_smart_refctd_ptr(std::move(layout), shaders, shaders+2u, vtxParams, blendParams, SPrimitiveAssemblyParams{}, SRasterizationParams{}); } return ppln; -#endif } +#endif namespace { @@ -403,6 +401,7 @@ namespace } } +#if 0 const char* CGraphicsPipelineLoaderMTL::readTexture(const char* _bufPtr, const char* const _bufEnd, SMtl* _currMaterial, const char* _mapType) const { static const std::unordered_map str2type = @@ -912,3 +911,4 @@ auto CGraphicsPipelineLoaderMTL::readMaterials(system::IFile* _file, const syste return materials; } +#endif diff --git a/src/nbl/asset/interchange/CGraphicsPipelineLoaderMTL.h b/src/nbl/asset/interchange/CGraphicsPipelineLoaderMTL.h index 3eb4ffea51..b95632f269 100644 --- a/src/nbl/asset/interchange/CGraphicsPipelineLoaderMTL.h +++ b/src/nbl/asset/interchange/CGraphicsPipelineLoaderMTL.h @@ -1,18 +1,20 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_C_GRAPHICS_PIPELINE_LOADER_MTL_H_INCLUDED_ +#define _NBL_ASSET_C_GRAPHICS_PIPELINE_LOADER_MTL_H_INCLUDED_ -#ifndef __NBL_ASSET_C_GRAPHICS_PIPELINE_LOADER_MTL_H_INCLUDED__ -#define __NBL_ASSET_C_GRAPHICS_PIPELINE_LOADER_MTL_H_INCLUDED__ -#include "nbl/asset/interchange/IRenderpassIndependentPipelineLoader.h" +#include "nbl/asset/interchange/IGeometryLoader.h" #include "nbl/asset/metadata/CMTLMetadata.h" + namespace nbl::asset { -class CGraphicsPipelineLoaderMTL final : public asset::IRenderpassIndependentPipelineLoader +class CGraphicsPipelineLoaderMTL final : public IAssetLoader // TODO: Material Asset and Loader { +#if 0 struct SMtl { CMTLMetadata::CRenderpassIndependentPipeline::SMaterialParameters params; @@ -25,7 +27,7 @@ class CGraphicsPipelineLoaderMTL final : public asset::IRenderpassIndependentPip inline bool isClampToBorder(CMTLMetadata::CRenderpassIndependentPipeline::E_MAP_TYPE m) const { return (clamp >> m) & 1u; } }; - +#endif struct SContext { SContext(const IAssetLoader::SAssetLoadContext& _innerCtx, uint32_t _topHierarchyLevel, IAssetLoader::IAssetLoaderOverride* _override) @@ -39,8 +41,6 @@ class CGraphicsPipelineLoaderMTL final : public asset::IRenderpassIndependentPip public: CGraphicsPipelineLoaderMTL(IAssetManager* _am, core::smart_refctd_ptr&& sys); - void initialize() override; - bool isALoadableFileFormat(system::IFile* _file, const system::logger_opt_ptr logger=nullptr) const override; const char** getAssociatedFileExtensions() const override @@ -49,11 +49,12 @@ class CGraphicsPipelineLoaderMTL final : public asset::IRenderpassIndependentPip return extensions; } - uint64_t getSupportedAssetTypesBitfield() const override { return asset::IAsset::ET_RENDERPASS_INDEPENDENT_PIPELINE; } +// uint64_t getSupportedAssetTypesBitfield() const override { return asset::IAsset::ET_MATERIAL; } asset::SAssetBundle loadAsset(system::IFile* _file, const asset::IAssetLoader::SAssetLoadParams& _params, asset::IAssetLoader::IAssetLoaderOverride* _override, uint32_t _hierarchyLevel = 0u) override; private: +#if 0 core::smart_refctd_ptr makePipelineFromMtl(SContext& ctx, const SMtl& _mtl, bool hasUV); core::vector readMaterials(system::IFile* _file, const system::logger_opt_ptr logger) const; const char* readTexture(const char* _bufPtr, const char* const _bufEnd, SMtl* _currMaterial, const char* _mapType) const; @@ -62,7 +63,8 @@ class CGraphicsPipelineLoaderMTL final : public asset::IRenderpassIndependentPip using image_views_set_t = std::array, CMTLMetadata::CRenderpassIndependentPipeline::EMP_REFL_POSX + 1u>; image_views_set_t loadImages(const std::string& relDir, SMtl& _mtl, SContext& _ctx); core::smart_refctd_ptr makeDescSet(image_views_set_t&& _views, ICPUDescriptorSetLayout* _dsLayout, SContext& _ctx); - private: +#endif + core::smart_refctd_ptr m_system; }; diff --git a/src/nbl/asset/interchange/COBJMeshFileLoader.cpp b/src/nbl/asset/interchange/COBJMeshFileLoader.cpp index 61d9eab6ec..69651f8061 100644 --- a/src/nbl/asset/interchange/COBJMeshFileLoader.cpp +++ b/src/nbl/asset/interchange/COBJMeshFileLoader.cpp @@ -6,14 +6,13 @@ #include "nbl/core/declarations.h" #include "nbl/asset/IAssetManager.h" -#include "nbl/asset/utils/IMeshManipulator.h" +#include "nbl/asset/utils/CPolygonGeometryManipulator.h" #ifdef _NBL_COMPILE_WITH_OBJ_LOADER_ #include "nbl/system/ISystem.h" #include "nbl/system/IFile.h" -#include "nbl/asset/metadata/COBJMetadata.h" #include "nbl/asset/utils/CQuantNormalCache.h" #include "COBJMeshFileLoader.h" diff --git a/src/nbl/asset/interchange/COBJMeshFileLoader.h b/src/nbl/asset/interchange/COBJMeshFileLoader.h index 47d7683997..c11a09e671 100644 --- a/src/nbl/asset/interchange/COBJMeshFileLoader.h +++ b/src/nbl/asset/interchange/COBJMeshFileLoader.h @@ -1,13 +1,12 @@ -// Copyright (C) 2019 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2019-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine" and was originally part of the "Irrlicht Engine" // For conditions of distribution and use, see copyright notice in nabla.h // See the original file in irrlicht source for authors - -#ifndef __NBL_ASSET_C_OBJ_MESH_FILE_LOADER_H_INCLUDED__ -#define __NBL_ASSET_C_OBJ_MESH_FILE_LOADER_H_INCLUDED__ +#ifndef _NBL_ASSET_C_OBJ_MESH_FILE_LOADER_H_INCLUDED_ +#define _NBL_ASSET_C_OBJ_MESH_FILE_LOADER_H_INCLUDED_ #include "nbl/core/declarations.h" -#include "nbl/asset/ICPUMeshBuffer.h" +#include "nbl/asset/ICPUPolygonGeometry.h" #include "nbl/asset/interchange/IAssetLoader.h" #include "nbl/asset/metadata/CMTLMetadata.h" @@ -53,7 +52,7 @@ class SObjVertex #include "nbl/nblunpack.h" //! Meshloader capable of loading obj meshes. -class COBJMeshFileLoader : public asset::IAssetLoader +class COBJMeshFileLoader : public IGeometryLoader { struct SContext { @@ -91,8 +90,6 @@ class COBJMeshFileLoader : public asset::IAssetLoader return ext; } - virtual uint64_t getSupportedAssetTypesBitfield() const override { return asset::IAsset::ET_MESH; } - virtual asset::SAssetBundle loadAsset(system::IFile* _file, const asset::IAssetLoader::SAssetLoadParams& _params, asset::IAssetLoader::IAssetLoaderOverride* _override = nullptr, uint32_t _hierarchyLevel = 0u) override; private: diff --git a/src/nbl/asset/interchange/CPLYMeshFileLoader.h b/src/nbl/asset/interchange/CPLYMeshFileLoader.h index ba699f044f..d80ae4dd9b 100644 --- a/src/nbl/asset/interchange/CPLYMeshFileLoader.h +++ b/src/nbl/asset/interchange/CPLYMeshFileLoader.h @@ -1,4 +1,4 @@ -// Copyright (C) 2019 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2019-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine" and was originally part of the "Irrlicht Engine" // For conditions of distribution and use, see copyright notice in nabla.h // See the original file in irrlicht source for authors @@ -7,13 +7,14 @@ #include "nbl/core/declarations.h" #include "nbl/asset/interchange/IAssetLoader.h" -#include "nbl/asset/ICPUMeshBuffer.h" -#include "nbl/asset/interchange/IRenderpassIndependentPipelineLoader.h" +#include "nbl/asset/ICPUPolygonGeometry.h" #include "nbl/asset/metadata/CPLYMetadata.h" namespace nbl::asset { +// TODO: move these global enums and constants to within the class + // input buffer must be at least twice as long as the longest line in the file #define PLY_INPUT_BUFFER_SIZE 51200 // file is loaded in 50k chunks @@ -29,33 +30,28 @@ enum E_PLY_PROPERTY_TYPE }; //! Meshloader capable of loading obj meshes. -class CPLYMeshFileLoader : public IRenderpassIndependentPipelineLoader +class CPLYMeshFileLoader final : public IGeometryLoader { -protected: - //! Destructor - virtual ~CPLYMeshFileLoader(); - -public: - //! Constructor - CPLYMeshFileLoader(IAssetManager* _am); + protected: + //! Destructor + virtual ~CPLYMeshFileLoader(); - virtual bool isALoadableFileFormat(system::IFile* _file, const system::logger_opt_ptr logger) const override; + public: + //! Constructor + CPLYMeshFileLoader(IAssetManager* _am); - virtual const char** getAssociatedFileExtensions() const override - { - static const char* ext[]{ "ply", nullptr }; - return ext; - } + virtual bool isALoadableFileFormat(system::IFile* _file, const system::logger_opt_ptr logger) const override; - virtual uint64_t getSupportedAssetTypesBitfield() const override { return IAsset::ET_MESH; } + virtual const char** getAssociatedFileExtensions() const override + { + static const char* ext[]{ "ply", nullptr }; + return ext; + } - //! creates/loads an animated mesh from the file. - virtual SAssetBundle loadAsset(system::IFile* _file, const IAssetLoader::SAssetLoadParams& _params, IAssetLoader::IAssetLoaderOverride* _override = nullptr, uint32_t _hierarchyLevel = 0u) override; + //! creates/loads an animated mesh from the file. + virtual SAssetBundle loadAsset(system::IFile* _file, const IAssetLoader::SAssetLoadParams& _params, IAssetLoader::IAssetLoaderOverride* _override = nullptr, uint32_t _hierarchyLevel = 0u) override; private: - - virtual void initialize() override; - enum E_TYPE { ET_POS = 0, ET_UV = 2, ET_NORM = 3, ET_COL = 1 }; static const std::string getPipelineCacheKey(E_TYPE type, bool indexBufferBindingAvailable) @@ -199,13 +195,13 @@ class CPLYMeshFileLoader : public IRenderpassIndependentPipelineLoader float getFloat(SContext& _ctx, E_PLY_PROPERTY_TYPE t); uint32_t getInt(SContext& _ctx, E_PLY_PROPERTY_TYPE t); void moveForward(SContext& _ctx, uint32_t bytes); - +#if 0 bool genVertBuffersForMBuffer( ICPUMeshBuffer* _mbuf, const asset::SBufferBinding attributes[4], SContext& context ) const; - +#endif template static inline void performActionBasedOnOrientationSystem(aType& varToHandle, void (*performOnCertainOrientation)(aType& varToHandle)) { @@ -214,5 +210,4 @@ class CPLYMeshFileLoader : public IRenderpassIndependentPipelineLoader }; } // end namespace nbl::asset - #endif diff --git a/src/nbl/asset/interchange/CPLYMeshWriter.h b/src/nbl/asset/interchange/CPLYMeshWriter.h index 1de4e14142..e709ffa0fe 100644 --- a/src/nbl/asset/interchange/CPLYMeshWriter.h +++ b/src/nbl/asset/interchange/CPLYMeshWriter.h @@ -1,26 +1,24 @@ -// Copyright (C) 2019 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2019-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine" and was originally part of the "Irrlicht Engine" // For conditions of distribution and use, see copyright notice in nabla.h // See the original file in irrlicht source for authors +#ifndef _NBL_ASSET_PLY_MESH_WRITER_H_INCLUDED_ +#define _NBL_ASSET_PLY_MESH_WRITER_H_INCLUDED_ -#ifndef __NBL_ASSET_PLY_MESH_WRITER_H_INCLUDED__ -#define __NBL_ASSET_PLY_MESH_WRITER_H_INCLUDED__ + +#include "nbl/asset/ICPUPolygonGeometry.h" +#include "nbl/asset/interchange/IGeometryWriter.h" #include -#include "nbl/asset/ICPUMeshBuffer.h" -#include "nbl/asset/interchange/IAssetWriter.h" -namespace nbl -{ -namespace asset +namespace nbl::asset { //! class to write PLY mesh files -class CPLYMeshWriter : public asset::IAssetWriter +class CPLYMeshWriter : public IGeometryWriter { public: - CPLYMeshWriter(); virtual const char** getAssociatedFileExtensions() const @@ -29,8 +27,6 @@ class CPLYMeshWriter : public asset::IAssetWriter return ext; } - virtual uint64_t getSupportedAssetTypesBitfield() const override { return asset::IAsset::ET_MESH; } - virtual uint32_t getSupportedFlags() override { return asset::EWF_BINARY; } virtual uint32_t getForcedFlags() { return 0u; } @@ -45,13 +41,13 @@ class CPLYMeshWriter : public asset::IAssetWriter size_t fileOffset = 0; }; - void writeBinary(const asset::ICPUMeshBuffer* _mbuf, size_t _vtxCount, size_t _fcCount, asset::E_INDEX_TYPE _idxType, void* const _indices, bool _forceFaces, const bool _vaidToWrite[4], SContext& context) const; - void writeText(const asset::ICPUMeshBuffer* _mbuf, size_t _vtxCount, size_t _fcCount, asset::E_INDEX_TYPE _idxType, void* const _indices, bool _forceFaces, const bool _vaidToWrite[4], SContext& context) const; + void writeBinary(const ICPUPolygonGeometry* geom, size_t _vtxCount, size_t _fcCount, asset::E_INDEX_TYPE _idxType, void* const _indices, bool _forceFaces, const bool _vaidToWrite[4], SContext& context) const; + void writeText(const ICPUPolygonGeometry* geom, size_t _vtxCount, size_t _fcCount, asset::E_INDEX_TYPE _idxType, void* const _indices, bool _forceFaces, const bool _vaidToWrite[4], SContext& context) const; - void writeAttribBinary(SContext& context, asset::ICPUMeshBuffer* _mbuf, uint32_t _vaid, size_t _ix, size_t _cpa, bool flipAttribute = false) const; + void writeAttribBinary(SContext& context, ICPUPolygonGeometry* geom, uint32_t _vaid, size_t _ix, size_t _cpa, bool flipAttribute = false) const; - //! Creates new mesh buffer with the same attribute buffers mapped but with normalized types changed to corresponding true integer types. - static core::smart_refctd_ptr createCopyMBuffNormalizedReplacedWithTrueInt(const asset::ICPUMeshBuffer* _mbuf); + //! Creates new geometry with the same attribute buffers mapped but with normalized types changed to corresponding true integer types. + static core::smart_refctd_ptr createCopyNormalizedReplacedWithTrueInt(const ICPUPolygonGeometry* geom); static std::string getTypeString(asset::E_FORMAT _t); @@ -80,6 +76,4 @@ class CPLYMeshWriter : public asset::IAssetWriter }; } // end namespace -} // end namespace - #endif diff --git a/src/nbl/asset/interchange/CSTLMeshFileLoader.h b/src/nbl/asset/interchange/CSTLMeshFileLoader.h index fc35134237..d811cd7e90 100644 --- a/src/nbl/asset/interchange/CSTLMeshFileLoader.h +++ b/src/nbl/asset/interchange/CSTLMeshFileLoader.h @@ -1,24 +1,22 @@ -// Copyright (C) 2019 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2019-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine" and was originally part of the "Irrlicht Engine" // For conditions of distribution and use, see copyright notice in nabla.h // See the original file in irrlicht source for authors +#ifndef _NBL_ASSET_C_STL_MESH_FILE_LOADER_H_INCLUDED_ +#define _NBL_ASSET_C_STL_MESH_FILE_LOADER_H_INCLUDED_ -#ifndef __NBL_ASSET_C_STL_MESH_FILE_LOADER_H_INCLUDED__ -#define __NBL_ASSET_C_STL_MESH_FILE_LOADER_H_INCLUDED__ #include "nbl/core/declarations.h" -#include "nbl/asset/interchange/IAssetLoader.h" -#include "nbl/asset/ICPUMeshBuffer.h" -#include "nbl/asset/interchange/IRenderpassIndependentPipelineLoader.h" + +#include "nbl/asset/interchange/IGeometryLoader.h" #include "nbl/asset/metadata/CSTLMetadata.h" -namespace nbl -{ -namespace asset + +namespace nbl::asset { //! Meshloader capable of loading STL meshes. -class CSTLMeshFileLoader final : public IRenderpassIndependentPipelineLoader +class CSTLMeshFileLoader final : public IGeometryLoader { public: @@ -34,10 +32,7 @@ class CSTLMeshFileLoader final : public IRenderpassIndependentPipelineLoader return ext; } - uint64_t getSupportedAssetTypesBitfield() const override { return IAsset::ET_MESH; } - private: - struct SContext { IAssetLoader::SAssetLoadContext inner; @@ -70,8 +65,6 @@ class CSTLMeshFileLoader final : public IRenderpassIndependentPipelineLoader asset::IAssetManager* m_assetMgr; }; -} // end namespace scene -} // end namespace nbl - +} // end namespace nbl::scene #endif diff --git a/src/nbl/asset/interchange/CSTLMeshWriter.h b/src/nbl/asset/interchange/CSTLMeshWriter.h index 3278c5c4f5..a25a84534c 100644 --- a/src/nbl/asset/interchange/CSTLMeshWriter.h +++ b/src/nbl/asset/interchange/CSTLMeshWriter.h @@ -1,21 +1,20 @@ -// Copyright (C) 2019 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2019-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine" and was originally part of the "Irrlicht Engine" // For conditions of distribution and use, see copyright notice in nabla.h // See the original file in irrlicht source for authors +#ifndef _NBL_ASSET_STL_MESH_WRITER_H_INCLUDED_ +#define _NBL_ASSET_STL_MESH_WRITER_H_INCLUDED_ -#ifndef __NBL_ASSET_STL_MESH_WRITER_H_INCLUDED__ -#define __NBL_ASSET_STL_MESH_WRITER_H_INCLUDED__ -#include "nbl/asset/ICPUMesh.h" -#include "nbl/asset/interchange/IAssetWriter.h" +#include "nbl/asset/ICPUPolygonGeometry.h" +#include "nbl/asset/interchange/IGeometryWriter.h" -namespace nbl -{ -namespace asset + +namespace nbl::asset { //! class to write meshes, implementing a STL writer -class CSTLMeshWriter : public asset::IAssetWriter +class CSTLMeshWriter : public IGeometryWriter { protected: virtual ~CSTLMeshWriter(); @@ -29,8 +28,6 @@ class CSTLMeshWriter : public asset::IAssetWriter return ext; } - virtual uint64_t getSupportedAssetTypesBitfield() const override { return asset::IAsset::ET_MESH; } - virtual uint32_t getSupportedFlags() override { return asset::EWF_BINARY; } virtual uint32_t getForcedFlags() { return 0u; } @@ -46,20 +43,17 @@ class CSTLMeshWriter : public asset::IAssetWriter }; // write binary format - bool writeMeshBinary(const asset::ICPUMesh* mesh, SContext* context); + bool writeMeshBinary(const ICPUPolygonGeometry* geom, SContext* context); // write text format - bool writeMeshASCII(const asset::ICPUMesh* mesh, SContext* context); + bool writeMeshASCII(const ICPUPolygonGeometry* geom, SContext* context); // create vector output with line end into string void getVectorAsStringLine(const core::vectorSIMDf& v, std::string& s) const; // write face information to file - void writeFaceText(const core::vectorSIMDf& v1, - const core::vectorSIMDf& v2, const core::vectorSIMDf& v3, SContext* context); + void writeFaceText(const core::vectorSIMDf& v1, const core::vectorSIMDf& v2, const core::vectorSIMDf& v3, SContext* context); }; } // end namespace -} // end namespace - #endif diff --git a/src/nbl/asset/interchange/IRenderpassIndependentPipelineLoader.cpp b/src/nbl/asset/interchange/IRenderpassIndependentPipelineLoader.cpp deleted file mode 100644 index 7c09ef88cc..0000000000 --- a/src/nbl/asset/interchange/IRenderpassIndependentPipelineLoader.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#include "nbl/asset/interchange/IRenderpassIndependentPipelineLoader.h" -#include "nbl/asset/asset_utils.h" - -using namespace nbl; -using namespace asset; - -IRenderpassIndependentPipelineLoader::~IRenderpassIndependentPipelineLoader() -{ -} - -void IRenderpassIndependentPipelineLoader::initialize() -{ - auto dfltOver = IAssetLoaderOverride(m_assetMgr); - const IAssetLoader::SAssetLoadContext fakeCtx(IAssetLoader::SAssetLoadParams{},nullptr); - - // find ds1 layout - auto ds1layout = dfltOver.findDefaultAsset("nbl/builtin/descriptor_set_layout/basic_view_parameters",fakeCtx,0u).first; - - // create common metadata part - { - constexpr size_t DS1_METADATA_ENTRY_CNT = 3ull; - m_basicViewParamsSemantics = core::make_refctd_dynamic_array(DS1_METADATA_ENTRY_CNT); - - constexpr IRenderpassIndependentPipelineMetadata::E_COMMON_SHADER_INPUT types[DS1_METADATA_ENTRY_CNT] = - { - IRenderpassIndependentPipelineMetadata::ECSI_WORLD_VIEW_PROJ, - IRenderpassIndependentPipelineMetadata::ECSI_WORLD_VIEW, - IRenderpassIndependentPipelineMetadata::ECSI_WORLD_VIEW_INVERSE_TRANSPOSE - }; - constexpr uint32_t sizes[DS1_METADATA_ENTRY_CNT] = - { - sizeof(SBasicViewParameters::MVP), - sizeof(SBasicViewParameters::MV), - sizeof(SBasicViewParameters::NormalMat) - }; - constexpr uint32_t relOffsets[DS1_METADATA_ENTRY_CNT] = - { - offsetof(SBasicViewParameters,MVP), - offsetof(SBasicViewParameters,MV), - offsetof(SBasicViewParameters,NormalMat) - }; - for (uint32_t i=0u; iend()-i-1u)[0]; - semantic.type = types[i]; - semantic.descriptorSection.type = IRenderpassIndependentPipelineMetadata::ShaderInput::E_TYPE::ET_UNIFORM_BUFFER; - semantic.descriptorSection.uniformBufferObject.binding = ds1layout->getDescriptorRedirect(IDescriptor::E_TYPE::ET_UNIFORM_BUFFER).getBinding(asset::ICPUDescriptorSetLayout::CBindingRedirect::storage_range_index_t{ 0 }).data; - semantic.descriptorSection.uniformBufferObject.set = 1u; - semantic.descriptorSection.uniformBufferObject.relByteoffset = relOffsets[i]; - semantic.descriptorSection.uniformBufferObject.bytesize = sizes[i]; - semantic.descriptorSection.shaderAccessFlags = hlsl::ShaderStage::ESS_VERTEX; - } - } -} diff --git a/src/nbl/asset/pch_asset.h b/src/nbl/asset/pch_asset.h index afd80bce51..361df786f1 100644 --- a/src/nbl/asset/pch_asset.h +++ b/src/nbl/asset/pch_asset.h @@ -1,9 +1,8 @@ // Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_PCH_ASSET_H_INCLUDED__ -#define __NBL_ASSET_PCH_ASSET_H_INCLUDED__ +#ifndef _NBL_ASSET_PCH_ASSET_H_INCLUDED_ +#define _NBL_ASSET_PCH_ASSET_H_INCLUDED_ #include "nbl/asset/asset.h" @@ -33,8 +32,6 @@ #include "nbl/asset/utils/IShaderCompiler.h" -// mesh -#include "nbl/asset/utils/CGeometryCreator.h" // loaders #include "nbl/asset/interchange/COBJMeshFileLoader.h" #include "nbl/asset/interchange/CPLYMeshFileLoader.h" @@ -43,10 +40,10 @@ #include "nbl/asset/interchange/CPLYMeshWriter.h" #include "nbl/asset/interchange/CSTLMeshWriter.h" // manipulation +#include "nbl/asset/utils/CPolygonGeometryManipulator.h" #include "nbl/asset/utils/CForsythVertexCacheOptimizer.h" #include "nbl/asset/utils/CSmoothNormalGenerator.h" -#include "nbl/asset/utils/COverdrawMeshOptimizer.h" -#include "nbl/asset/utils/CMeshManipulator.h" +#include "nbl/asset/utils/COverdrawPolygonGeometryOptimizer.h" #endif //_NBL_PCH_IGNORE_PRIVATE_HEADERS diff --git a/src/nbl/asset/utils/CGLSLCompiler.cpp b/src/nbl/asset/utils/CGLSLCompiler.cpp index ca6ee329a8..723bad2a7b 100644 --- a/src/nbl/asset/utils/CGLSLCompiler.cpp +++ b/src/nbl/asset/utils/CGLSLCompiler.cpp @@ -61,20 +61,18 @@ namespace nbl::asset::impl shaderc_include_result* res = new shaderc_include_result; std::filesystem::path relDir; - #ifdef NBL_EMBED_BUILTIN_RESOURCES + #ifndef NBL_EMBED_BUILTIN_RESOURCES + const bool reqBuiltin = false; + #else const bool reqFromBuiltin = builtin::hasPathPrefix(_requesting_source); const bool reqBuiltin = builtin::hasPathPrefix(_requested_source); + //While #includ'ing a builtin, one must specify its full path (starting with "nbl/builtin" or "/nbl/builtin"). + // This rule applies also while a builtin is #includ`ing another builtin. + //While including a filesystem file it must be either absolute path (or relative to any search dir added to asset::iIncludeHandler; <>-type), + // or path relative to executable's working directory (""-type). if (!reqFromBuiltin && !reqBuiltin) - { - //While #includ'ing a builtin, one must specify its full path (starting with "nbl/builtin" or "/nbl/builtin"). - // This rule applies also while a builtin is #includ`ing another builtin. - //While including a filesystem file it must be either absolute path (or relative to any search dir added to asset::iIncludeHandler; <>-type), - // or path relative to executable's working directory (""-type). + #endif // NBL_EMBED_BUILTIN_RESOURCES relDir = std::filesystem::path(_requesting_source).parent_path(); - } - #else - const bool reqBuiltin = false; - #endif // NBL_EMBED_BUILTIN_RESOURCES std::filesystem::path name = (_type == shaderc_include_type_relative) ? (relDir / _requested_source) : (_requested_source); if (std::filesystem::exists(name) && !reqBuiltin) diff --git a/src/nbl/asset/utils/CGeometryCreator.cpp b/src/nbl/asset/utils/CGeometryCreator.cpp index ea18b4bf2b..c5c6ac6765 100644 --- a/src/nbl/asset/utils/CGeometryCreator.cpp +++ b/src/nbl/asset/utils/CGeometryCreator.cpp @@ -2,44 +2,33 @@ // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h + +#include "nbl/asset/utils/CGeometryCreator.h" +#include "nbl/builtin/hlsl/tgmath.hlsl" + #include #include #include +#include -#include "nbl/asset/utils/CGeometryCreator.h" -#include "nbl/asset/utils/CQuantNormalCache.h" namespace nbl::asset { -CGeometryCreator::CGeometryCreator(IMeshManipulator* const _defaultMeshManipulator) - : defaultMeshManipulator(_defaultMeshManipulator) -{ - if (defaultMeshManipulator == nullptr) - { - _NBL_DEBUG_BREAK_IF(true); - assert(false); - } -} - -CGeometryCreator::return_type CGeometryCreator::createCubeMesh(const core::vector3df& size) const +core::smart_refctd_ptr CGeometryCreator::createCube(const hlsl::float32_t3 size) const { - return_type retval; + using namespace hlsl; - constexpr size_t vertexSize = sizeof(CGeometryCreator::CubeVertex); - retval.inputParams = {0b1111u,0b1u,{ - {0u,EF_R32G32B32_SFLOAT,offsetof(CubeVertex,pos)}, - {0u,EF_R8G8B8A8_UNORM,offsetof(CubeVertex,color)}, - {0u,EF_R8G8_USCALED,offsetof(CubeVertex,uv)}, - {0u,EF_R8G8B8_SSCALED,offsetof(CubeVertex,normal)} - },{vertexSize,SVertexInputBindingParams::EVIR_PER_VERTEX}}; + auto retval = core::make_smart_refctd_ptr(); + retval->setIndexing(IPolygonGeometryBase::TriangleList()); // Create indices + using index_t = uint16_t; { - retval.indexCount = 36u; - auto indices = asset::ICPUBuffer::create({ sizeof(uint16_t)*retval.indexCount }); - indices->addUsageFlags(asset::IBuffer::EUF_INDEX_BUFFER_BIT); - auto u = reinterpret_cast(indices->getPointer()); + constexpr auto IndexCount = 36u; + constexpr auto bytesize = sizeof(index_t) * IndexCount; + auto indices = ICPUBuffer::create({bytesize,IBuffer::EUF_INDEX_BUFFER_BIT}); + auto u = reinterpret_cast(indices->getPointer()); for (uint32_t i=0u; i<6u; ++i) { u[i*6+0] = 4*i+0; @@ -49,118 +38,170 @@ CGeometryCreator::return_type CGeometryCreator::createCubeMesh(const core::vecto u[i*6+4] = 4*i+2; u[i*6+5] = 4*i+3; } - retval.indexBuffer = {0ull,std::move(indices)}; + shapes::AABB<4,index_t> aabb; + aabb.minVx[0] = 0; + aabb.maxVx[0] = 23; + retval->setIndexView({ + .composed = { + .encodedDataRange = {.u16=aabb}, + .stride = sizeof(index_t), + .format = EF_R16_UINT, + .rangeFormat = IGeometryBase::EAABBFormat::U16 + }, + .src = {.offset=0,.size=bytesize,.buffer=std::move(indices)} + }); } - // Create vertices - auto vertices = asset::ICPUBuffer::create({ 24u*vertexSize }); - vertices->addUsageFlags(IBuffer::EUF_VERTEX_BUFFER_BIT); - CubeVertex* ptr = (CubeVertex*)vertices->getPointer(); + constexpr auto CubeUniqueVertices = 24; - const core::vector3d normals[6] = - { - core::vector3d(0, 0, 1), - core::vector3d(1, 0, 0), - core::vector3d(0, 0, -1), - core::vector3d(-1, 0, 0), - core::vector3d(0, 1, 0), - core::vector3d(0, -1, 0) - }; - const core::vector3df pos[8] = - { - core::vector3df(-0.5f,-0.5f, 0.5f)*size, - core::vector3df( 0.5f,-0.5f, 0.5f)*size, - core::vector3df( 0.5f, 0.5f, 0.5f)*size, - core::vector3df(-0.5f, 0.5f, 0.5f)*size, - core::vector3df( 0.5f,-0.5f,-0.5f)*size, - core::vector3df(-0.5f, 0.5f,-0.5f)*size, - core::vector3df(-0.5f,-0.5f,-0.5f)*size, - core::vector3df( 0.5f, 0.5f,-0.5f)*size - }; - const core::vector2d uvs[4] = + // Create vertex attributes with NONE usage because we have no clue how they'll be used + hlsl::float32_t3* positions; + // for now because no reliable RGB10A2 encode and scant support for 24-bit UTB formats + hlsl::vector* normals; + hlsl::vector* uvs; { - core::vector2d(0, 1), - core::vector2d(1, 1), - core::vector2d(1, 0), - core::vector2d(0, 0) - }; + { + constexpr auto AttrSize = sizeof(decltype(*positions)); + auto buff = ICPUBuffer::create({AttrSize*CubeUniqueVertices,IBuffer::EUF_NONE}); + positions = reinterpret_cast(buff->getPointer()); + shapes::AABB<4,float32_t> aabb; + aabb.maxVx = float32_t4(size*0.5f,0.f); + aabb.minVx = -aabb.maxVx; + retval->setPositionView({ + .composed = { + .encodedDataRange = {.f32=aabb}, + .stride = AttrSize, + .format = EF_R32G32B32_SFLOAT, + .rangeFormat = IGeometryBase::EAABBFormat::F32 + }, + .src = {.offset=0,.size=buff->getSize(),.buffer = std::move(buff)} + }); + } + { + constexpr auto AttrSize = sizeof(decltype(*normals)); + auto buff = ICPUBuffer::create({AttrSize*CubeUniqueVertices,IBuffer::EUF_NONE}); + normals = reinterpret_cast(buff->getPointer()); + shapes::AABB<4,int8_t> aabb; + aabb.maxVx = hlsl::vector(127,127,127,0); + aabb.minVx = -aabb.maxVx; + retval->setNormalView({ + .composed = { + .encodedDataRange = {.s8=aabb}, + .stride = AttrSize, + .format = EF_R8G8B8A8_SNORM, + .rangeFormat = IGeometryBase::EAABBFormat::S8_NORM + }, + .src = {.offset=0,.size=buff->getSize(),.buffer=std::move(buff)} + }); + } + { + constexpr auto AttrSize = sizeof(decltype(*uvs)); + auto buff = ICPUBuffer::create({AttrSize*CubeUniqueVertices,IBuffer::EUF_NONE}); + uvs = reinterpret_cast(buff->getPointer()); + shapes::AABB<4,uint8_t> aabb; + aabb.minVx = hlsl::vector(0,0,0,0); + aabb.maxVx = hlsl::vector(255,255,0,0); + retval->getAuxAttributeViews()->push_back({ + .composed = { + .encodedDataRange = {.u8=aabb}, + .stride = AttrSize, + .format = EF_R8G8_UNORM, + .rangeFormat = IGeometryBase::EAABBFormat::U8_NORM + }, + .src = {.offset=0,.size=buff->getSize(),.buffer=std::move(buff)} + }); + } + } - for (size_t f=0ull; f<6ull; ++f) + // { - const size_t v = f*4ull; - - for (size_t i=0ull; i<4ull; ++i) + const hlsl::float32_t3 pos[8] = { - const core::vector3d& n = normals[f]; - const core::vector2d& uv = uvs[i]; - ptr[v+i].setColor(255, 255, 255, 255); - ptr[v+i].setNormal(n.X, n.Y, n.Z); - ptr[v+i].setUv(uv.X, uv.Y); - } + hlsl::float32_t3(-0.5f,-0.5f, 0.5f) * size, + hlsl::float32_t3(0.5f,-0.5f, 0.5f) * size, + hlsl::float32_t3(0.5f, 0.5f, 0.5f) * size, + hlsl::float32_t3(-0.5f, 0.5f, 0.5f) * size, + hlsl::float32_t3(0.5f,-0.5f,-0.5f) * size, + hlsl::float32_t3(-0.5f, 0.5f,-0.5f) * size, + hlsl::float32_t3(-0.5f,-0.5f,-0.5f) * size, + hlsl::float32_t3(0.5f, 0.5f,-0.5f) * size + }; + positions[0] = hlsl::float32_t3(pos[0][0], pos[0][1], pos[0][2]); + positions[1] = hlsl::float32_t3(pos[1][0], pos[1][1], pos[1][2]); + positions[2] = hlsl::float32_t3(pos[2][0], pos[2][1], pos[2][2]); + positions[3] = hlsl::float32_t3(pos[3][0], pos[3][1], pos[3][2]); + positions[4] = hlsl::float32_t3(pos[1][0], pos[1][1], pos[1][2]); + positions[5] = hlsl::float32_t3(pos[4][0], pos[4][1], pos[4][2]); + positions[6] = hlsl::float32_t3(pos[7][0], pos[7][1], pos[7][2]); + positions[7] = hlsl::float32_t3(pos[2][0], pos[2][1], pos[2][2]); + positions[8] = hlsl::float32_t3(pos[4][0], pos[4][1], pos[4][2]); + positions[9] = hlsl::float32_t3(pos[6][0], pos[6][1], pos[6][2]); + positions[10] = hlsl::float32_t3(pos[5][0], pos[5][1], pos[5][2]); + positions[11] = hlsl::float32_t3(pos[7][0], pos[7][1], pos[7][2]); + positions[12] = hlsl::float32_t3(pos[6][0], pos[6][1], pos[6][2]); + positions[13] = hlsl::float32_t3(pos[0][0], pos[0][1], pos[0][2]); + positions[14] = hlsl::float32_t3(pos[3][0], pos[3][1], pos[3][2]); + positions[15] = hlsl::float32_t3(pos[5][0], pos[5][1], pos[5][2]); + positions[16] = hlsl::float32_t3(pos[3][0], pos[3][1], pos[3][2]); + positions[17] = hlsl::float32_t3(pos[2][0], pos[2][1], pos[2][2]); + positions[18] = hlsl::float32_t3(pos[7][0], pos[7][1], pos[7][2]); + positions[19] = hlsl::float32_t3(pos[5][0], pos[5][1], pos[5][2]); + positions[20] = hlsl::float32_t3(pos[0][0], pos[0][1], pos[0][2]); + positions[21] = hlsl::float32_t3(pos[6][0], pos[6][1], pos[6][2]); + positions[22] = hlsl::float32_t3(pos[4][0], pos[4][1], pos[4][2]); + positions[23] = hlsl::float32_t3(pos[1][0], pos[1][1], pos[1][2]); + } - switch (f) + // + { + const hlsl::vector norm[6] = + { + hlsl::vector(0, 0, 127), + hlsl::vector(127, 0, 0), + hlsl::vector(0, 0,-127), + hlsl::vector(-127, 0, 0), + hlsl::vector(0, 127, 0), + hlsl::vector(0,-127, 0) + }; + const hlsl::vector uv[4] = { - case 0: - ptr[v+0].setPos(pos[0].X, pos[0].Y, pos[0].Z); - ptr[v+1].setPos(pos[1].X, pos[1].Y, pos[1].Z); - ptr[v+2].setPos(pos[2].X, pos[2].Y, pos[2].Z); - ptr[v+3].setPos(pos[3].X, pos[3].Y, pos[3].Z); - break; - case 1: - ptr[v+0].setPos(pos[1].X, pos[1].Y, pos[1].Z); - ptr[v+1].setPos(pos[4].X, pos[4].Y, pos[4].Z); - ptr[v+2].setPos(pos[7].X, pos[7].Y, pos[7].Z); - ptr[v+3].setPos(pos[2].X, pos[2].Y, pos[2].Z); - break; - case 2: - ptr[v+0].setPos(pos[4].X, pos[4].Y, pos[4].Z); - ptr[v+1].setPos(pos[6].X, pos[6].Y, pos[6].Z); - ptr[v+2].setPos(pos[5].X, pos[5].Y, pos[5].Z); - ptr[v+3].setPos(pos[7].X, pos[7].Y, pos[7].Z); - break; - case 3: - ptr[v+0].setPos(pos[6].X, pos[6].Y, pos[6].Z); - ptr[v+2].setPos(pos[3].X, pos[3].Y, pos[3].Z); - ptr[v+1].setPos(pos[0].X, pos[0].Y, pos[0].Z); - ptr[v+3].setPos(pos[5].X, pos[5].Y, pos[5].Z); - break; - case 4: - ptr[v+0].setPos(pos[3].X, pos[3].Y, pos[3].Z); - ptr[v+1].setPos(pos[2].X, pos[2].Y, pos[2].Z); - ptr[v+2].setPos(pos[7].X, pos[7].Y, pos[7].Z); - ptr[v+3].setPos(pos[5].X, pos[5].Y, pos[5].Z); - break; - case 5: - ptr[v+0].setPos(pos[0].X, pos[0].Y, pos[0].Z); - ptr[v+1].setPos(pos[6].X, pos[6].Y, pos[6].Z); - ptr[v+2].setPos(pos[4].X, pos[4].Y, pos[4].Z); - ptr[v+3].setPos(pos[1].X, pos[1].Y, pos[1].Z); - break; + hlsl::vector( 0,255), + hlsl::vector(255,255), + hlsl::vector(255, 0), + hlsl::vector( 0, 0) + }; + for (size_t f=0ull; f<6ull; ++f) + { + const size_t v = f*4ull; + + for (size_t i=0ull; i<4ull; ++i) + { + normals[v+i] = vector(norm[f],0); + uvs[v+i] = uv[i]; + } } } - retval.bindings[0] = {0ull,std::move(vertices)}; - - // Recalculate bounding box - retval.indexType = asset::EIT_16BIT; - retval.bbox = core::aabbox3df(-size*0.5f,size*0.5f); + CPolygonGeometryManipulator::recomputeContentHashes(retval.get()); return retval; } +#if 0 /* a cylinder, a cone and a cross point up on (0,1.f, 0.f ) */ -CGeometryCreator::return_type CGeometryCreator::createArrowMesh(const uint32_t tesselationCylinder, - const uint32_t tesselationCone, - const float height, - const float cylinderHeight, - const float width0, - const float width1, - const video::SColor vtxColor0, - const video::SColor vtxColor1, - IMeshManipulator* const meshManipulatorOverride) const +core::smart_refctd_ptr CGeometryCreator::createArrow( + const uint32_t tesselationCylinder, + const uint32_t tesselationCone, + const float height, + const float cylinderHeight, + const float width0, + const float width1, + const video::SColor vtxColor0, + const video::SColor vtxColor1 +) const { assert(height > cylinderHeight); @@ -248,7 +289,7 @@ CGeometryCreator::return_type CGeometryCreator::createArrowMesh(const uint32_t t } /* A sphere with proper normals and texture coords */ -CGeometryCreator::return_type CGeometryCreator::createSphereMesh(float radius, uint32_t polyCountX, uint32_t polyCountY, IMeshManipulator* const meshManipulatorOverride) const +core::smart_refctd_ptr CGeometryCreator::createSphere(float radius, uint32_t polyCountX, uint32_t polyCountY, IMeshManipulator* const meshManipulatorOverride) const { // we are creating the sphere mesh here. return_type retval; @@ -372,7 +413,7 @@ CGeometryCreator::return_type CGeometryCreator::createSphereMesh(float radius, u { // calculate points position - core::vector3df pos(static_cast(cos(axz) * sinay), + float32_t3 pos(static_cast(cos(axz) * sinay), static_cast(cos(ay)), static_cast(sin(axz) * sinay)); // for spheres the normal is the position @@ -435,7 +476,7 @@ CGeometryCreator::return_type CGeometryCreator::createSphereMesh(float radius, u // recalculate bounding box core::aabbox3df BoundingBox; - BoundingBox.reset(core::vector3df(radius)); + BoundingBox.reset(float32_t3(radius)); BoundingBox.addInternalPoint(-radius, -radius, -radius); // set vertex buffer @@ -449,8 +490,10 @@ CGeometryCreator::return_type CGeometryCreator::createSphereMesh(float radius, u } /* A cylinder with proper normals and texture coords */ -CGeometryCreator::return_type CGeometryCreator::createCylinderMesh(float radius, float length, - uint32_t tesselation, const video::SColor& color, IMeshManipulator* const meshManipulatorOverride) const +core::smart_refctd_ptr CGeometryCreator::createCylinder( + float radius, float length, + uint32_t tesselation, const video::SColor& color, IMeshManipulator* const meshManipulatorOverride +) const { return_type retval; constexpr size_t vertexSize = sizeof(CGeometryCreator::CylinderVertex); @@ -519,11 +562,13 @@ CGeometryCreator::return_type CGeometryCreator::createCylinderMesh(float radius, } /* A cone with proper normals and texture coords */ -CGeometryCreator::return_type CGeometryCreator::createConeMesh( float radius, float length, uint32_t tesselation, - const video::SColor& colorTop, - const video::SColor& colorBottom, - float oblique, - IMeshManipulator* const meshManipulatorOverride) const +core::smart_refctd_ptr CGeometryCreator::createCone( + float radius, float length, uint32_t tesselation, + const video::SColor& colorTop, + const video::SColor& colorBottom, + float oblique, + IMeshManipulator* const meshManipulatorOverride +) const { const size_t vtxCnt = tesselation * 2; auto vtxBuf = asset::ICPUBuffer::create({ vtxCnt * sizeof(ConeVertex) }); @@ -604,113 +649,211 @@ CGeometryCreator::return_type CGeometryCreator::createConeMesh( float radius, fl return cone; } +#endif - -CGeometryCreator::return_type CGeometryCreator::createRectangleMesh(const core::vector2df_SIMD& _size) const +core::smart_refctd_ptr CGeometryCreator::createRectangle(const hlsl::float32_t2 size) const { - return_type retval; - constexpr size_t vertexSize = sizeof(CGeometryCreator::RectangleVertex); - retval.inputParams = { 0b1111u,0b1u,{ - {0u,EF_R32G32B32_SFLOAT,offsetof(RectangleVertex,pos)}, - {0u,EF_R8G8B8A8_UNORM,offsetof(RectangleVertex,color)}, - {0u,EF_R8G8_USCALED,offsetof(RectangleVertex,uv)}, - {0u,EF_R32G32B32_SFLOAT,offsetof(RectangleVertex,normal)} - },{vertexSize,SVertexInputBindingParams::EVIR_PER_VERTEX} }; - // Create indices - retval.indexCount = 6; - retval.indexType = asset::EIT_16BIT; - uint16_t u[6]; + using namespace hlsl; - /* - 0---1 - | / | - 3---2 - */ - u[0] = 0; - u[1] = 3; - u[2] = 1; - u[3] = 1; - u[4] = 3; - u[5] = 2; - - auto indices = asset::ICPUBuffer::create({ sizeof(u) }); - memcpy(indices->getPointer(), u, sizeof(u)); - indices->addUsageFlags(asset::IBuffer::EUF_INDEX_BUFFER_BIT); - retval.indexBuffer = { 0ull, std::move(indices) }; + auto retval = core::make_smart_refctd_ptr(); + retval->setIndexing(IPolygonGeometryBase::TriangleList()); + + // Create indices + { + using index_t = uint16_t; + /* + 0---1 + | / | + 3---2 + */ + const index_t indices[] = {0,3,1,1,3,2}; + auto buffer = ICPUBuffer::create({ + {sizeof(indices),IBuffer::EUF_INDEX_BUFFER_BIT}, + const_cast((const void*)indices) // TODO: temporary till two different creation params (adopting needs non const void, copying needs const void only + }); + shapes::AABB<4,index_t> aabb; + aabb.minVx[0] = 0; + aabb.maxVx[0] = 3; + retval->setIndexView({ + .composed = { + .encodedDataRange = {.u16=aabb}, + .stride = sizeof(index_t), + .format = EF_R16_UINT, + .rangeFormat = IGeometryBase::EAABBFormat::U16 + }, + .src = {.offset=0,.size=buffer->getSize(),.buffer=std::move(buffer)} + }); + } // Create vertices - auto vertices = asset::ICPUBuffer::create({ 4 * vertexSize }); - RectangleVertex* ptr = (RectangleVertex*)vertices->getPointer(); - - ptr[0] = RectangleVertex(core::vector3df_SIMD(-1.0f, 1.0f, 0.0f) * _size, video::SColor(0xFFFFFFFFu), - core::vector2du32_SIMD(0u, 1u), core::vector3df_SIMD(0.0f, 0.0f, 1.0f)); - ptr[1] = RectangleVertex(core::vector3df_SIMD( 1.0f, 1.0f, 0.0f) * _size, video::SColor(0xFFFFFFFFu), - core::vector2du32_SIMD(1u, 1u), core::vector3df_SIMD(0.0f, 0.0f, 1.0f)); - ptr[2] = RectangleVertex(core::vector3df_SIMD( 1.0f, -1.0f, 0.0f) * _size, video::SColor(0xFFFFFFFFu), - core::vector2du32_SIMD(1u, 0u), core::vector3df_SIMD(0.0f, 0.0f, 1.0f)); - ptr[3] = RectangleVertex(core::vector3df_SIMD(-1.0f, -1.0f, 0.0f) * _size, video::SColor(0xFFFFFFFFu), - core::vector2du32_SIMD(0u, 0u), core::vector3df_SIMD(0.0f, 0.0f, 1.0f)); - - vertices->addUsageFlags(asset::IBuffer::EUF_VERTEX_BUFFER_BIT); - retval.bindings[0] = {0ull, std::move(vertices)}; + { + { + const hlsl::float32_t2 positions[] = { + hlsl::float32_t2(-size.x, size.y), + hlsl::float32_t2( size.x, size.y), + hlsl::float32_t2( size.x,-size.y), + hlsl::float32_t2(-size.x,-size.y) + }; + auto buff = ICPUBuffer::create({{sizeof(positions),IBuffer::EUF_NONE},(void*)positions}); + shapes::AABB<4,float32_t> aabb; + aabb.minVx = float32_t4(-size,0.f,0.f); + aabb.maxVx = float32_t4( size,0.f,0.f); + retval->setPositionView({ + .composed = { + .encodedDataRange = {.f32=aabb}, + .stride = sizeof(positions[0]), + .format = EF_R32G32_SFLOAT, + .rangeFormat = IGeometryBase::EAABBFormat::F32 + }, + .src = {.offset=0,.size=buff->getSize(),.buffer = std::move(buff)} + }); + } + { + const hlsl::vector normals[] = { + hlsl::vector(0,0,127,0), + hlsl::vector(0,0,127,0), + hlsl::vector(0,0,127,0), + hlsl::vector(0,0,127,0) + }; + auto buff = ICPUBuffer::create({{sizeof(normals),IBuffer::EUF_NONE},(void*)normals}); + shapes::AABB<4,int8_t> aabb; + aabb.maxVx = hlsl::vector(0,0,127,0); + aabb.minVx = -aabb.maxVx; + retval->setNormalView({ + .composed = { + .encodedDataRange = {.s8=aabb}, + .stride = sizeof(normals[0]), + .format = EF_R8G8B8A8_SNORM, + .rangeFormat = IGeometryBase::EAABBFormat::S8_NORM + }, + .src = {.offset=0,.size=buff->getSize(),.buffer=std::move(buff)} + }); + } + { + const hlsl::vector uvs[] = { + hlsl::vector( 0,255), + hlsl::vector(255,255), + hlsl::vector(255, 0), + hlsl::vector( 0, 0) + }; + auto buff = ICPUBuffer::create({{sizeof(uvs),IBuffer::EUF_NONE},(void*)uvs}); + shapes::AABB<4,uint8_t> aabb; + aabb.minVx = hlsl::vector(0,0,0,0); + aabb.maxVx = hlsl::vector(255,255,0,0); + retval->getAuxAttributeViews()->push_back({ + .composed = { + .encodedDataRange = {.u8=aabb}, + .stride = sizeof(uvs[0]), + .format = EF_R8G8_UNORM, + .rangeFormat = IGeometryBase::EAABBFormat::U8_NORM + }, + .src = {.offset=0,.size=buff->getSize(),.buffer=std::move(buff)} + }); + } + } + CPolygonGeometryManipulator::recomputeContentHashes(retval.get()); return retval; } -CGeometryCreator::return_type CGeometryCreator::createDiskMesh(float radius, uint32_t tesselation) const +core::smart_refctd_ptr CGeometryCreator::createDisk(const float radius, const uint32_t tesselation) const { - return_type retval; - constexpr size_t vertexSize = sizeof(CGeometryCreator::DiskVertex); + // need at least 120 external angles in the fan + if (tesselation<2) + return nullptr; - retval.inputParams = { 0b1111u,0b1u,{ - {0u,EF_R32G32B32_SFLOAT,offsetof(DiskVertex,pos)}, - {0u,EF_R8G8B8A8_UNORM,offsetof(DiskVertex,color)}, - {0u,EF_R8G8_USCALED,offsetof(DiskVertex,uv)}, - {0u,EF_R32G32B32_SFLOAT,offsetof(DiskVertex,normal)} - },{vertexSize,SVertexInputBindingParams::EVIR_PER_VERTEX} }; - retval.assemblyParams.primitiveType = EPT_TRIANGLE_FAN; // without indices - retval.indexType = EIT_UNKNOWN; + using namespace hlsl; - const size_t vertexCount = 2u + tesselation; - retval.indexCount = vertexCount; - - const float angle = 360.0f / static_cast(tesselation); - - auto vertices = asset::ICPUBuffer::create({ vertexCount * vertexSize }); - DiskVertex* ptr = (DiskVertex*)vertices->getPointer(); - - const core::vectorSIMDf v0(0.0f, radius, 0.0f, 1.0f); - core::matrix3x4SIMD rotation; - - //center - ptr[0] = DiskVertex(core::vector3df_SIMD(0.0f), video::SColor(0xFFFFFFFFu), - core::vector2du32_SIMD(0u, 1u), core::vector3df_SIMD(0.0f, 0.0f, 1.0f)); + auto retval = core::make_smart_refctd_ptr(); + retval->setIndexing(IPolygonGeometryBase::TriangleFan()); - //v0 - ptr[1] = DiskVertex(v0, video::SColor(0xFFFFFFFFu), - core::vector2du32_SIMD(0u, 1u), core::vector3df_SIMD(0.0f, 0.0f, 1.0f)); - - //vn - ptr[vertexCount - 1] = ptr[1]; + // without index buffer + const size_t vertexCount = 2u + tesselation; - //v1, v2, ..., vn-1 - for (int i = 2; i < vertexCount-1; i++) + float32_t2* positions; + // for now because no reliable RGB10A2 encode and scant support for 24-bit UTB formats + hlsl::vector* normals; + // + constexpr uint16_t UnityUV = 0xffffu; + uint16_t2* uvs; { - core::vectorSIMDf vn; - core::matrix3x4SIMD rotMatrix; - rotMatrix.setRotation(core::quaternion(0.0f, 0.0f, core::radians((i-1)*angle))); - rotMatrix.transformVect(vn, v0); - - ptr[i] = DiskVertex(vn, video::SColor(0xFFFFFFFFu), - core::vector2du32_SIMD(0u, 1u), core::vector3df_SIMD(0.0f, 0.0f, 1.0f)); + { + constexpr auto AttrSize = sizeof(decltype(*positions)); + auto buff = ICPUBuffer::create({AttrSize*vertexCount,IBuffer::EUF_NONE}); + positions = reinterpret_cast(buff->getPointer()); + shapes::AABB<4,float32_t> aabb; + aabb.maxVx = float32_t4(radius,radius,0.f,0.f); + aabb.minVx = -aabb.maxVx; + retval->setPositionView({ + .composed = { + .encodedDataRange = {.f32=aabb}, + .stride = AttrSize, + .format = EF_R32G32_SFLOAT, + .rangeFormat = IGeometryBase::EAABBFormat::F32 + }, + .src = {.offset=0,.size=buff->getSize(),.buffer = std::move(buff)} + }); + } + { + constexpr auto AttrSize = sizeof(decltype(*normals)); + auto buff = ICPUBuffer::create({AttrSize*vertexCount,IBuffer::EUF_NONE}); + normals = reinterpret_cast(buff->getPointer()); + shapes::AABB<4,int8_t> aabb; + aabb.maxVx = hlsl::vector(0,0,127,0); + aabb.minVx = -aabb.maxVx; + retval->setNormalView({ + .composed = { + .encodedDataRange = {.s8=aabb}, + .stride = AttrSize, + .format = EF_R8G8B8A8_SNORM, + .rangeFormat = IGeometryBase::EAABBFormat::S8_NORM + }, + .src = {.offset=0,.size=buff->getSize(),.buffer=std::move(buff)} + }); + } + { + constexpr auto AttrSize = sizeof(decltype(*uvs)); + auto buff = ICPUBuffer::create({AttrSize*vertexCount,IBuffer::EUF_NONE}); + uvs = reinterpret_cast(buff->getPointer()); + shapes::AABB<4,uint16_t> aabb; + aabb.minVx = uint16_t4(0,0,0,0); + aabb.maxVx = uint16_t4(UnityUV,UnityUV,0,0); + retval->getAuxAttributeViews()->push_back({ + .composed = { + .encodedDataRange = {.u16=aabb}, + .stride = AttrSize, + .format = EF_R16G16_UNORM, + .rangeFormat = IGeometryBase::EAABBFormat::U16_NORM + }, + .src = {.offset=0,.size=buff->getSize(),.buffer=std::move(buff)} + }); + } } - vertices->addUsageFlags(asset::IBuffer::EUF_VERTEX_BUFFER_BIT); - retval.bindings[0] = {0ull, std::move(vertices)}; + // populate data + { + const float angle = 360.f / static_cast(tesselation); + // center + *(positions++) = float32_t2(0.f,0.f); + *(uvs++) = uint16_t2(0,UnityUV); + // last + positions[tesselation] = float32_t3(0.f,radius,0.f); + uvs[tesselation] = uint16_t2(UnityUV,0); + for (auto i=0; i; + *(positions++) = float32_t2(hlsl::sin(rad),hlsl::cos(rad))*radius; + *(uvs++) = uint16_t2(t*UnityUV+0.5f,0); + } + } + std::fill_n(normals,vertexCount,hlsl::vector(0,0,127,0)); + CPolygonGeometryManipulator::recomputeContentHashes(retval.get()); return retval; } +#if 0 /* Helpful Icosphere class implementation used to compute and create icopshere's vertices and indecies. @@ -1675,7 +1818,7 @@ class Icosphere }; -CGeometryCreator::return_type CGeometryCreator::createIcoSphere(float radius, uint32_t subdivision, bool smooth) const +core::smart_refctd_ptr CGeometryCreator::createIcoSphere(float radius, uint32_t subdivision, bool smooth) const { Icosphere IcosphereData(radius, subdivision, smooth); @@ -1708,7 +1851,7 @@ CGeometryCreator::return_type CGeometryCreator::createIcoSphere(float radius, ui return icosphereGeometry; } - +#endif } // end namespace nbl::asset diff --git a/src/nbl/asset/utils/CGeometryCreator.h b/src/nbl/asset/utils/CGeometryCreator.h deleted file mode 100644 index 50ddcb646c..0000000000 --- a/src/nbl/asset/utils/CGeometryCreator.h +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_C_GEOMETRY_CREATOR_H_INCLUDED__ -#define __NBL_ASSET_C_GEOMETRY_CREATOR_H_INCLUDED__ - -#include "nbl/asset/utils/IGeometryCreator.h" - -namespace nbl -{ - -namespace asset -{ - -//! class for creating geometry on the fly -class CGeometryCreator : public IGeometryCreator -{ - public: - #include "nbl/nblpack.h" - struct CubeVertex - { - float pos[3]; - uint8_t color[4]; // normalized - uint8_t uv[2]; - int8_t normal[3]; - uint8_t dummy[3]; - - void setPos(float x, float y, float z) { pos[0] = x; pos[1] = y; pos[2] = z; } - void translate(float dx, float dy, float dz) { pos[0] += dx; pos[1] += dy; pos[2] += dz; } - void setColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { color[0] = r; color[1] = g; color[2] = b; color[3] = a; } - void setNormal(int8_t x, int8_t y, int8_t z) { normal[0] = x; normal[1] = y; normal[2] = z; } - void setUv(uint8_t u, uint8_t v) { uv[0] = u; uv[1] = v; } - } PACK_STRUCT; - - public: - CGeometryCreator(IMeshManipulator* const _defaultMeshManipulator); - - private: - struct RectangleVertex - { - RectangleVertex(const core::vector3df_SIMD& _pos, const video::SColor& _color, const core::vector2du32_SIMD _uv, const core::vector3df_SIMD _normal) - { - memcpy(pos, _pos.pointer, sizeof(float) * 3); - _color.toOpenGLColor(color); - uv[0] = _uv.x; - uv[1] = _uv.y; - normal[0] = _normal.x; - normal[1] = _normal.y; - normal[2] = _normal.z; - } - float pos[3]; - uint8_t color[4]; - uint8_t uv[2]; - uint8_t dummy[2]; - float normal[3]; - } PACK_STRUCT; - - typedef RectangleVertex DiskVertex; - - struct ConeVertex - { - inline ConeVertex(const core::vectorSIMDf& _pos, const CQuantNormalCache::value_type_t& _nml, const video::SColor& _color) : normal{_nml} - { - memcpy(pos, _pos.pointer, 12); - _color.toOpenGLColor(color); - } - - float pos[3]; - uint8_t color[4]; - CQuantNormalCache::value_type_t normal; - } PACK_STRUCT; - - struct CylinderVertex - { - CylinderVertex() : pos{0.f, 0.f, 0.f}, color{0u, 0u, 0u, 0u}, uv{0.f, 0.f}, normal() {} - - float pos[3]; - uint8_t color[4]; - float uv[2]; - CQuantNormalCache::value_type_t normal; - } PACK_STRUCT; - - struct IcosphereVertex - { - IcosphereVertex() : pos{ 0.f, 0.f, 0.f }, normals{ 0.f, 0.f, 0.f }, uv{ 0.f, 0.f } {} - - float pos[3]; - float normals[3]; - float uv[2]; - } PACK_STRUCT; - #include "nbl/nblunpack.h" - - - using SphereVertex = CylinderVertex; - using ArrowVertex = CylinderVertex; - - //smart_refctd_ptr? - IMeshManipulator* const defaultMeshManipulator; - - public: - return_type createCubeMesh(const core::vector3df& size) const override; - - return_type createArrowMesh(const uint32_t tesselationCylinder, - const uint32_t tesselationCone, const float height, - const float cylinderHeight, const float width0, - const float width1, const video::SColor vtxColor0, - const video::SColor vtxColor1, - IMeshManipulator* const meshManipulatorOverride = nullptr) const override; - - return_type createSphereMesh(float radius, uint32_t polyCountX, uint32_t polyCountY, IMeshManipulator* const meshManipulatorOverride = nullptr) const override; - - return_type createCylinderMesh( float radius, float length, uint32_t tesselation, - const video::SColor& color=0xffffffff, - IMeshManipulator* const meshManipulatorOverride = nullptr) const override; - - return_type createConeMesh( float radius, float length, uint32_t tesselation, - const video::SColor& colorTop=0xffffffff, - const video::SColor& colorBottom=0xffffffff, - float oblique=0.f, - IMeshManipulator* const meshManipulatorOverride = nullptr) const override; - - return_type createRectangleMesh(const core::vector2df_SIMD& _size = core::vector2df_SIMD(0.5f, 0.5f)) const override; - - return_type createDiskMesh(float radius, uint32_t tesselation) const override; - - return_type createIcoSphere(float radius = 1.0f, uint32_t subdivision = 1, bool smooth = false) const override; - -}; - -} // end namespace asset -} // end namespace nbl - -#endif - diff --git a/src/nbl/asset/utils/CMeshManipulator.h b/src/nbl/asset/utils/CMeshManipulator.h deleted file mode 100644 index 7a5b895665..0000000000 --- a/src/nbl/asset/utils/CMeshManipulator.h +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_C_MESH_MANIPULATOR_H_INCLUDED__ -#define __NBL_ASSET_C_MESH_MANIPULATOR_H_INCLUDED__ - -#include "nbl/asset/utils/IMeshManipulator.h" -#include "nbl/asset/utils/CQuantNormalCache.h" - -namespace nbl -{ -namespace asset -{ - -//! An interface for easy manipulation of meshes. -/** Scale, set alpha value, flip surfaces, and so on. This exists for fixing -problems with wrong imported or exported meshes quickly after loading. It is -not intended for doing mesh modifications and/or animations during runtime. -*/ -class CMeshManipulator : public IMeshManipulator -{ - struct SAttrib - { - E_FORMAT type; - E_FORMAT prevType; - size_t size; - uint32_t vaid; - size_t offset; - - SAttrib() : type(EF_UNKNOWN), size(0), vaid(ICPUMeshBuffer::MAX_VERTEX_ATTRIB_COUNT) {} - - friend bool operator>(const SAttrib& _a, const SAttrib& _b) { return _a.size > _b.size; } - }; - struct SAttribTypeChoice - { - E_FORMAT type; - }; - - public: - static core::smart_refctd_ptr createMeshBufferFetchOptimized(const ICPUMeshBuffer* _inbuffer); - - CQuantNormalCache* getQuantNormalCache() override { return &quantNormalCache; } - CQuantQuaternionCache* getQuantQuaternionCache() override { return &quantQuaternionCache; } - - private: - friend class IMeshManipulator; - - template - static void _filterInvalidTriangles(ICPUMeshBuffer* _input); - - //! Meant to create 32bit index buffer from subrange of index buffer containing 16bit indices. Remember to set to index buffer offset to 0 after mapping buffer resulting from this function. - static inline core::smart_refctd_ptr create32BitFrom16BitIdxBufferSubrange(const uint16_t* _in, uint32_t _idxCount) - { - if (!_in) - return nullptr; - - auto out = ICPUBuffer::create({ sizeof(uint32_t) * _idxCount }); - - auto* outPtr = reinterpret_cast(out->getPointer()); - - for (uint32_t i=0u; i<_idxCount; ++i) - outPtr[i] = _in[i]; - - return out; - } - - static core::vector findBetterFormatF(E_FORMAT* _outType, size_t* _outSize, E_FORMAT* _outPrevType, const ICPUMeshBuffer* _meshbuffer, uint32_t _attrId, const SErrorMetric& _errMetric, CQuantNormalCache& _cache); - - struct SIntegerAttr - { - uint32_t pointer[4]; - }; - static core::vector findBetterFormatI(E_FORMAT* _outType, size_t* _outSize, E_FORMAT* _outPrevType, const ICPUMeshBuffer* _meshbuffer, uint32_t _attrId, const SErrorMetric& _errMetric); - - //E_COMPONENT_TYPE getBestTypeF(bool _normalized, E_COMPONENTS_PER_ATTRIBUTE _cpa, size_t* _outSize, E_COMPONENTS_PER_ATTRIBUTE* _outCpa, const float* _min, const float* _max) const; - static E_FORMAT getBestTypeI(E_FORMAT _originalType, size_t* _outSize, const uint32_t* _min, const uint32_t* _max); - static core::vector findTypesOfProperRangeF(E_FORMAT _type, size_t _sizeThreshold, const float* _min, const float* _max, const SErrorMetric& _errMetric); - - //! Calculates quantization errors and compares them with given epsilon. - /** @returns false when first of calculated errors goes above epsilon or true if reached end without such. */ - static bool calcMaxQuantizationError(const SAttribTypeChoice& _srcType, const SAttribTypeChoice& _dstType, const core::vector& _data, const SErrorMetric& _errMetric, CQuantNormalCache& _cache); - - template - static inline core::smart_refctd_ptr lineStripsToLines(const void* _input, uint32_t& _idxCount) - { - const auto outputSize = _idxCount = (_idxCount - 1) * 2; - - auto output = ICPUBuffer::create({ sizeof(OutType)*outputSize }); - const auto* iptr = reinterpret_cast(_input); - auto* optr = reinterpret_cast(output->getPointer()); - for (uint32_t i = 0, j = 0; i < outputSize;) - { - optr[i++] = iptr[j++]; - optr[i++] = iptr[j]; - } - return output; - } - - template - static inline core::smart_refctd_ptr triangleStripsToTriangles(const void* _input, uint32_t& _idxCount) - { - const auto outputSize = _idxCount = (_idxCount - 2) * 3; - - auto output = ICPUBuffer::create({ sizeof(OutType)*outputSize }); - const auto* iptr = reinterpret_cast(_input); - auto* optr = reinterpret_cast(output->getPointer()); - for (uint32_t i = 0, j = 0; i < outputSize; j += 2) - { - optr[i++] = iptr[j + 0]; - optr[i++] = iptr[j + 1]; - optr[i++] = iptr[j + 2]; - if (i == outputSize) - break; - optr[i++] = iptr[j + 2]; - optr[i++] = iptr[j + 1]; - optr[i++] = iptr[j + 3]; - } - return output; - } - - template - static inline core::smart_refctd_ptr trianglesFanToTriangles(const void* _input, uint32_t& _idxCount) - { - const auto outputSize = _idxCount = (_idxCount - 2) * 3; - - auto output = ICPUBuffer::create({ sizeof(OutType)*outputSize }); - const auto* iptr = reinterpret_cast(_input); - auto* optr = reinterpret_cast(output->getPointer()); - for (uint32_t i = 0, j = 1; i < outputSize;) - { - optr[i++] = iptr[0]; - optr[i++] = iptr[j++]; - optr[i++] = iptr[j]; - } - return output; - } - - private: - CQuantNormalCache quantNormalCache; - CQuantQuaternionCache quantQuaternionCache; -}; - -} // end namespace scene -} // end namespace nbl - - -#endif diff --git a/src/nbl/asset/utils/COverdrawMeshOptimizer.cpp b/src/nbl/asset/utils/COverdrawPolygonGeometryOptimizer.cpp similarity index 98% rename from src/nbl/asset/utils/COverdrawMeshOptimizer.cpp rename to src/nbl/asset/utils/COverdrawPolygonGeometryOptimizer.cpp index 1929437279..3b140ff460 100644 --- a/src/nbl/asset/utils/COverdrawMeshOptimizer.cpp +++ b/src/nbl/asset/utils/COverdrawPolygonGeometryOptimizer.cpp @@ -1,19 +1,17 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h -#include "nbl/core/declarations.h" -#include "COverdrawMeshOptimizer.h" +#include "nbl/asset/utils/COverdrawPolygonGeometryOptimizer.h" #include #include -#include "CMeshManipulator.h" namespace nbl::asset { - +#if 0 void COverdrawMeshOptimizer::createOptimized(asset::ICPUMeshBuffer* _outbuffer, const asset::ICPUMeshBuffer* _inbuffer, float _threshold, const system::logger_opt_ptr logger) { if (!_outbuffer || !_inbuffer) @@ -267,5 +265,5 @@ size_t COverdrawMeshOptimizer::updateCache(uint32_t _a, uint32_t _b, uint32_t _c return cacheMisses; } - +#endif } // nbl::asset diff --git a/src/nbl/asset/utils/COverdrawMeshOptimizer.h b/src/nbl/asset/utils/COverdrawPolygonGeometryOptimizer.h similarity index 77% rename from src/nbl/asset/utils/COverdrawMeshOptimizer.h rename to src/nbl/asset/utils/COverdrawPolygonGeometryOptimizer.h index bf5ac15e84..9e751b6493 100644 --- a/src/nbl/asset/utils/COverdrawMeshOptimizer.h +++ b/src/nbl/asset/utils/COverdrawPolygonGeometryOptimizer.h @@ -1,20 +1,17 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_C_OVERDRAW_POLYGON_GEOMETRY_OPTIMIZER_H_INCLUDED_ +#define _NBL_ASSET_C_OVERDRAW_POLYGON_GEOMETRY_OPTIMIZER_H_INCLUDED_ -#ifndef __NBL_ASSET_C_OVERDRAW_MESH_OPTIMIZER_H_INCLUDED__ -#define __NBL_ASSET_C_OVERDRAW_MESH_OPTIMIZER_H_INCLUDED__ -#include "nbl/asset/ICPUMeshBuffer.h" +#include "nbl/asset/ICPUPolygonGeometry.h" -// Based on zeux's meshoptimizer (https://github.com/zeux/meshoptimizer) available under MIT license -namespace nbl -{ -namespace asset +namespace nbl::asset { - -class COverdrawMeshOptimizer +// Based on zeux's meshoptimizer (https://github.com/zeux/meshoptimizer) available under MIT license +class COverdrawPolygonGeometryOptimizer { _NBL_STATIC_INLINE_CONSTEXPR size_t CACHE_SIZE = 16; @@ -31,7 +28,7 @@ class COverdrawMeshOptimizer }; // private, undefined constructor - COverdrawMeshOptimizer() = delete; + COverdrawPolygonGeometryOptimizer() = delete; public: //! Creates new or modifies given mesh reordering indices to reduce pixel overdraw and vertex shader invocations. @@ -40,7 +37,7 @@ class COverdrawMeshOptimizer @param _createNew Flag deciding whether to create new mesh (not modifying given one) or just optimize given mesh. Defaulted to true (i.e. create new). @param _threshold Indicates how much the overdraw optimizer can degrade vertex cache efficiency (1.05 = up to 5%) to reduce overdraw more efficiently. Defaulted to 1.05 (i.e. 5%). */ - static void createOptimized(asset::ICPUMeshBuffer* _outbuffer, const asset::ICPUMeshBuffer* _inbuffer, float _threshold = 1.05f, const system::logger_opt_ptr logger = nullptr); + static void createOptimized(ICPUPolygonGeometry* out, const ICPUPolygonGeometry* in, float _threshold = 1.05f, const system::logger_opt_ptr logger = nullptr); private: template @@ -55,6 +52,4 @@ class COverdrawMeshOptimizer }; } -} - #endif \ No newline at end of file diff --git a/src/nbl/asset/utils/CMeshManipulator.cpp b/src/nbl/asset/utils/CPolygonGeometryManipulator.cpp similarity index 99% rename from src/nbl/asset/utils/CMeshManipulator.cpp rename to src/nbl/asset/utils/CPolygonGeometryManipulator.cpp index d06762bf86..1e08c172ba 100644 --- a/src/nbl/asset/utils/CMeshManipulator.cpp +++ b/src/nbl/asset/utils/CPolygonGeometryManipulator.cpp @@ -1,7 +1,10 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h + +#include "nbl/asset/asset.h" + #include #include #include @@ -9,17 +12,15 @@ #include #include - -#include "nbl/asset/asset.h" -#include "nbl/asset/IRenderpassIndependentPipeline.h" -#include "nbl/asset/utils/CMeshManipulator.h" +#include "nbl/asset/utils/CPolygonGeometryManipulator.h" #include "nbl/asset/utils/CSmoothNormalGenerator.h" #include "nbl/asset/utils/CForsythVertexCacheOptimizer.h" -#include "nbl/asset/utils/COverdrawMeshOptimizer.h" +#include "nbl/asset/utils/COverdrawPolygonGeometryOptimizer.h" + namespace nbl::asset { - +#if 0 //! Flips the direction of surfaces. Changes backfacing triangles to frontfacing //! triangles and vice versa. //! \param mesh: Mesh on which the operation is performed. @@ -1724,6 +1725,7 @@ core::matrix3x4SIMD IMeshManipulator::calculateOBB(const nbl::asset::ICPUMeshBuf return TransMat; } +#endif } // end namespace nbl::asset diff --git a/src/nbl/asset/utils/CSmoothNormalGenerator.cpp b/src/nbl/asset/utils/CSmoothNormalGenerator.cpp index 6dcb7ff69f..72c2a3acc8 100644 --- a/src/nbl/asset/utils/CSmoothNormalGenerator.cpp +++ b/src/nbl/asset/utils/CSmoothNormalGenerator.cpp @@ -14,7 +14,7 @@ namespace nbl { namespace asset { - +#if 0 static inline bool operator<(uint32_t lhs, const IMeshManipulator::SSNGVertexData& rhs) { return lhs < rhs.hash; @@ -281,6 +281,6 @@ std::array CSmoothNormalGenerator::VertexHashMap::getNeighboringCel } return neighbourhood; } - +#endif } } \ No newline at end of file diff --git a/src/nbl/asset/utils/CSmoothNormalGenerator.h b/src/nbl/asset/utils/CSmoothNormalGenerator.h index 1181a9011f..f921c4cea8 100644 --- a/src/nbl/asset/utils/CSmoothNormalGenerator.h +++ b/src/nbl/asset/utils/CSmoothNormalGenerator.h @@ -1,82 +1,73 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_C_SMOOTH_NORMAL_GENERATOR_H_INCLUDED_ +#define _NBL_ASSET_C_SMOOTH_NORMAL_GENERATOR_H_INCLUDED_ -#ifndef __NBL_ASSET_C_SMOOTH_NORMAL_GENERATOR_H_INCLUDED__ -#define __NBL_ASSET_C_SMOOTH_NORMAL_GENERATOR_H_INCLUDED__ +#include "nbl/asset/utils/CPolygonGeometryManipulator.h" -#include #include -#include "nbl/core/math/glslFunctions.h" -#include "nbl/asset/ICPUMeshBuffer.h" -#include "nbl/asset/utils/IMeshManipulator.h" - - -namespace nbl -{ -namespace asset +namespace nbl::asset { class CSmoothNormalGenerator { -public: - static core::smart_refctd_ptr calculateNormals(asset::ICPUMeshBuffer* buffer, float epsilon, uint32_t normalAttrID, IMeshManipulator::VxCmpFunction function); - - CSmoothNormalGenerator() = delete; - ~CSmoothNormalGenerator() = delete; - -private: - class VertexHashMap - { public: - struct BucketBounds - { - core::vector::iterator begin; - core::vector::iterator end; - }; + CSmoothNormalGenerator() = delete; + ~CSmoothNormalGenerator() = delete; +#if 0 + static core::smart_refctd_ptr calculateNormals(ICPUMeshBuffer* buffer, float epsilon, uint32_t normalAttrID, IMeshManipulator::VxCmpFunction function); - public: - VertexHashMap(size_t _vertexCount, uint32_t _hashTableMaxSize, float _cellSize); + private: + class VertexHashMap + { + public: + struct BucketBounds + { + core::vector::iterator begin; + core::vector::iterator end; + }; - //inserts vertex into hash table - void add(IMeshManipulator::SSNGVertexData&& vertex); + public: + VertexHashMap(size_t _vertexCount, uint32_t _hashTableMaxSize, float _cellSize); - //sorts hashtable and sets iterators at beginnings of bucktes - void validate(); + //inserts vertex into hash table + void add(IMeshManipulator::SSNGVertexData&& vertex); - // - std::array getNeighboringCellHashes(const IMeshManipulator::SSNGVertexData& vertex); + //sorts hashtable and sets iterators at beginnings of bucktes + void validate(); - inline uint32_t getBucketCount() const { return buckets.size(); } - inline BucketBounds getBucketBoundsById(uint32_t index) { return { buckets[index], buckets[index + 1] }; } - BucketBounds getBucketBoundsByHash(uint32_t hash); + // + std::array getNeighboringCellHashes(const IMeshManipulator::SSNGVertexData& vertex); - private: - static constexpr uint32_t invalidHash = 0xFFFFFFFF; + inline uint32_t getBucketCount() const { return buckets.size(); } + inline BucketBounds getBucketBoundsById(uint32_t index) { return { buckets[index], buckets[index + 1] }; } + BucketBounds getBucketBoundsByHash(uint32_t hash); - private: - //holds iterators pointing to beginning of each bucket, last iterator points to vertices.end() - core::vector::iterator> buckets; - core::vector vertices; - const uint32_t hashTableMaxSize; - const float cellSize; + private: + static constexpr uint32_t invalidHash = 0xFFFFFFFF; - private: - uint32_t hash(const IMeshManipulator::SSNGVertexData& vertex) const; - uint32_t hash(const core::vector3du32_SIMD& position) const; + private: + //holds iterators pointing to beginning of each bucket, last iterator points to vertices.end() + core::vector::iterator> buckets; + core::vector vertices; + const uint32_t hashTableMaxSize; + const float cellSize; - }; + private: + uint32_t hash(const IMeshManipulator::SSNGVertexData& vertex) const; + uint32_t hash(const core::vector3du32_SIMD& position) const; -private: - static VertexHashMap setupData(const asset::ICPUMeshBuffer* buffer, float epsilon); - static void processConnectedVertices(asset::ICPUMeshBuffer* buffer, VertexHashMap& vertices, float epsilon, uint32_t normalAttrID, IMeshManipulator::VxCmpFunction vxcmp); + }; + private: + static VertexHashMap setupData(const ICPUMeshBuffer* buffer, float epsilon); + static void processConnectedVertices(ICPUMeshBuffer* buffer, VertexHashMap& vertices, float epsilon, uint32_t normalAttrID, IMeshManipulator::VxCmpFunction vxcmp); +#endif }; } -} - #endif \ No newline at end of file diff --git a/src/nbl/builtin/CMakeLists.txt b/src/nbl/builtin/CMakeLists.txt index fcbe58eb41..523491dfdb 100644 --- a/src/nbl/builtin/CMakeLists.txt +++ b/src/nbl/builtin/CMakeLists.txt @@ -316,6 +316,10 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/circle.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/ellipse.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/line.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/beziers.hlsl") +#sampling +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/concentric_mapping.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/cos_weighted.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/uniform.hlsl") # LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/ndarray_addressing.hlsl") # @@ -325,6 +329,14 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/fft/common.hlsl") #sort LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sort/common.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sort/counting.hlsl") +#bxdf +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/common.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/fresnel.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/geom_smith.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/ndf.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/reflection.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/transmission.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/bxdf_traits.hlsl") #subgroup LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/subgroup/ballot.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/subgroup/basic.hlsl") diff --git a/src/nbl/video/IPhysicalDevice.cpp b/src/nbl/video/IPhysicalDevice.cpp index ded519c1ca..dca2a47289 100644 --- a/src/nbl/video/IPhysicalDevice.cpp +++ b/src/nbl/video/IPhysicalDevice.cpp @@ -417,6 +417,9 @@ asset::E_FORMAT IPhysicalDevice::promoteBufferFormat(const SBufferFormatPromotio asset::E_FORMAT IPhysicalDevice::promoteImageFormat(const SImageFormatPromotionRequest req, const IGPUImage::TILING tiling) const { + if (req.originalFormat==asset::EF_UNKNOWN) + return req.originalFormat; + format_image_cache_t& cache = tiling==IGPUImage::TILING::LINEAR ? this->m_formatPromotionCache.linearTilingImages : this->m_formatPromotionCache.optimalTilingImages; diff --git a/src/nbl/video/utilities/CAssetConverter.cpp b/src/nbl/video/utilities/CAssetConverter.cpp index 72807c7739..3ce684e0b7 100644 --- a/src/nbl/video/utilities/CAssetConverter.cpp +++ b/src/nbl/video/utilities/CAssetConverter.cpp @@ -348,6 +348,14 @@ bool CAssetConverter::patch_impl_t::valid(const ILogicalDevi return !invalid; } +CAssetConverter::patch_impl_t::patch_impl_t(const ICPUPolygonGeometry* view) {} +bool CAssetConverter::patch_impl_t::valid(const ILogicalDevice* device) +{ + // patch only stores usages to propagate, the buffers themselves will check their patches + return true; +} + + // not sure if useful enough to move to core utils template struct index_of; @@ -377,6 +385,17 @@ struct instance_t size_t uniqueCopyGroupID = 0xdeadbeefBADC0FFEull; }; +enum class EPolygonGeometryViewType : uint8_t +{ + Position = 0, + Index = 1, + Normal = 2, + JointOBB = 3, + JointIndices = 4, + JointWeights = 5, + Aux = 6 +}; + // template class AssetVisitor : public CRTP @@ -675,6 +694,47 @@ class AssetVisitor : public CRTP } return true; } + inline bool impl(const instance_t& instance, const CAssetConverter::patch_t& userPatch) + { + const auto* geo = instance.asset; + if (!geo->valid()) + return false; + auto visit = [&](const IGeometry::SDataView& view, const core::bitflag& extraUsages, const EPolygonGeometryViewType type, const uint32_t index=0)->bool + { + if (!view) + return true; + const auto* dep = view.src.buffer.get(); + CAssetConverter::patch_t patch = {dep}; + patch.usage |= extraUsages; + return descend(dep,std::move(patch),type,index); + }; + if (!visit(geo->getPositionView(),userPatch.positionBufferUsages,EPolygonGeometryViewType::Position)) + return false; + if (!visit(geo->getIndexView(),userPatch.indexBufferUsages,EPolygonGeometryViewType::Index)) + return false; + if (!visit(geo->getNormalView(),userPatch.otherBufferUsages,EPolygonGeometryViewType::Normal)) + return false; + if (geo->isSkinned()) + { + if (geo->getJointOBBView()) + if (!visit(*geo->getJointOBBView(),userPatch.otherBufferUsages,EPolygonGeometryViewType::JointOBB)) + return false; + auto i=0; + for (const auto& entry : geo->getJointWeightViews()) + { + if (!visit(entry.indices,userPatch.otherBufferUsages,EPolygonGeometryViewType::JointIndices,i)) + return false; + if (!visit(entry.weights,userPatch.otherBufferUsages,EPolygonGeometryViewType::JointWeights,i)) + return false; + i++; + } + } + auto i = 0; + for (const auto& entry : geo->getAuxAttributeViews()) + if (!visit(entry,userPatch.otherBufferUsages,EPolygonGeometryViewType::Aux,i++)) + return false; + return true; + } }; @@ -995,6 +1055,7 @@ class PatchOverride final : public CAssetConverter::CHashCache::IPatchOverride inline const patch_t* operator()(const lookup_t& lookup) const override {return impl(lookup);} inline const patch_t* operator()(const lookup_t& lookup) const override {return impl(lookup);} inline const patch_t* operator()(const lookup_t& lookup) const override {return impl(lookup);} + inline const patch_t* operator()(const lookup_t& lookup) const override {return impl(lookup);} }; template @@ -1534,6 +1595,60 @@ bool CAssetConverter::CHashCache::hash_impl::operator()(lookup_t lookup) +{ + // NOTE: We don't hash the usage metada from the patch! Because it doesn't matter (applied on buffers). + // The view usage in the patch helps us propagate and patch during DFS, but no more. + const auto* asset = lookup.asset; + // we need to hash stuff that will let us disambiguate having views get visited in same order but from different slots + { + const auto* indexing = asset->getIndexingCallback(); + // the particular callback should be a static and we can rely on the pointer being unique for a particular indexing algorithm + hasher << ptrdiff_t(indexing); + } + // not hashing redundant things (which can be worked out from properties we hash) like AABB, Primitive Count, etc. + auto hashView = [&](const IGeometry::SDataView& view)->void + { + if (!view) + { + hasher << false; + return; + } + hasher << true; + hasher << view.composed; + hasher << view.src.offset; + hasher << view.src.actualSize(); + // don't hash the buffer itself, will get hashed by `visitor` below + }; + hashView(asset->getPositionView()); + hashView(asset->getIndexView()); + hashView(asset->getNormalView()); + hasher << asset->getJointCount(); + if (asset->isSkinned()) + { + if (asset->getJointOBBView()) + hashView(*asset->getJointOBBView()); + hasher << asset->getJointWeightViews().size(); + for (const auto& entry : asset->getJointWeightViews()) + { + hashView(entry.indices); + hashView(entry.weights); + } + } + hasher << asset->getAuxAttributeViews().size(); + for (const auto& entry : asset->getAuxAttributeViews()) + hashView(entry); + + AssetVisitor> visitor = { + *this, + {asset,static_cast(patchOverride)->uniqueCopyGroupID}, + *lookup.patch + }; + if (!visitor()) + return false; + return true; +} + void CAssetConverter::CHashCache::eraseStale(const IPatchOverride* patchOverride) { @@ -1572,6 +1687,7 @@ void CAssetConverter::CHashCache::eraseStale(const IPatchOverride* patchOverride rehash.template operator()(); rehash.template operator()(); // rehash.template operator()(); + rehash.template operator()(); } @@ -1934,6 +2050,86 @@ class GetDependantVisit : public GetDependantVisitBase +class GetDependantVisit : public GetDependantVisitBase +{ + public: + bool finalize() + { + if (!creationParams.indexing) + return false; + creationParams.jointWeightViews = jointWeightViews; + creationParams.auxAttributeViews = auxAttributeViews; + return true; + } + + IGPUPolygonGeometry::SCreationParams creationParams = {}; + // has to be public because of aggregate init, but its only for internal usage! + core::vector jointWeightViews = {}; + core::vector auxAttributeViews = {}; + + protected: + bool descend_impl( + const instance_t& user, const CAssetConverter::patch_t& userPatch, + const instance_t& dep, const CAssetConverter::patch_t& soloPatch, + const EPolygonGeometryViewType type, const uint32_t index + ) + { + auto depObj = getDependant(dep,soloPatch); + if (!depObj) + return false; + const auto* asset = user.asset; + switch (type) + { + case EPolygonGeometryViewType::Position: + // obligatory attribute, handle basic setup here too + creationParams.indexing = asset->getIndexingCallback(); + creationParams.jointCount = asset->getJointCount(); + creationParams.positionView = getView(asset->getPositionView(),std::move(depObj)); + break; + case EPolygonGeometryViewType::Index: + creationParams.indexView = getView(asset->getIndexView(),std::move(depObj)); + break; + case EPolygonGeometryViewType::Normal: + creationParams.normalView = getView(asset->getNormalView(),std::move(depObj)); + break; + case EPolygonGeometryViewType::JointOBB: + creationParams.jointOBBView = getView(*asset->getJointOBBView(),std::move(depObj)); + break; + case EPolygonGeometryViewType::JointIndices: + jointWeightViews.resize(index+1); + jointWeightViews[index].indices = getView(asset->getJointWeightViews()[index].indices,std::move(depObj)); + break; + case EPolygonGeometryViewType::JointWeights: + jointWeightViews.resize(index+1); + jointWeightViews[index].weights = getView(asset->getJointWeightViews()[index].weights,std::move(depObj)); + break; + case EPolygonGeometryViewType::Aux: + auxAttributeViews.push_back(getView(asset->getAuxAttributeViews()[index],std::move(depObj))); + break; + default: + return false; + } + // abuse this pointer to signal invalid state + return creationParams.indexing; + } + + private: + IGPUPolygonGeometry::SDataView getView(const ICPUPolygonGeometry::SDataView& orig, core::smart_refctd_ptr&& buff) + { + IGPUPolygonGeometry::SDataView retval = { + .composed = orig.composed, + .src = { + .offset = orig.src.offset, + .size = orig.src.actualSize(), + .buffer = std::move(buff) + } + }; + if (orig && !retval) + creationParams.indexing = nullptr; + return retval; + } +}; // Needed both for reservation and conversion @@ -2405,12 +2601,9 @@ struct conversions_t if (!deferredAllocator->request(output,constrainMask)) return; } - - if constexpr (!std::is_same_v) - { - // set debug names on everything - setDebugName(conv,output->get(),contentHash,uniqueCopyGroupID); - } + // set debug names on everything! + if constexpr (std::is_base_of_v::video_t>) + setDebugName(conv,output->get(),contentHash,uniqueCopyGroupID); } // Since the dfsCache has the original asset pointers as keys, we map in reverse (multiple `instance_t` can map to the same unique content hash and GPU object) @@ -2587,6 +2780,9 @@ auto CAssetConverter::reserve(const SInputs& inputs) -> SReserveResult case ICPUTopLevelAccelerationStructure::AssetType: visit.template operator()(entry); break; + case ICPUPolygonGeometry::AssetType: + visit.template operator()(entry); + break; // these assets have no dependants, should have never been pushed on the stack default: assert(false); @@ -3318,6 +3514,27 @@ auto CAssetConverter::reserve(const SInputs& inputs) -> SReserveResult } } } + if constexpr (std::is_same_v) + { + for (auto& entry : conversionRequests.contentHashToCanonical) + { + const ICPUPolygonGeometry* asset = entry.second.canonicalAsset; + const auto& patch = dfsCache.nodes[entry.second.patchIndex.value].patch; + for (auto i=0ull; i> visitor = { + {visitBase}, + {asset,uniqueCopyGroupID}, + patch + }; + if (!visitor() || !visitor.finalize()) + continue; + conversionRequests.assign(entry.first,entry.second.firstCopyIx,i,IGPUPolygonGeometry::create(std::move(visitor.creationParams))); + } + } + } // clear what we don't need if constexpr (!std::is_base_of_v) @@ -3456,6 +3673,7 @@ auto CAssetConverter::reserve(const SInputs& inputs) -> SReserveResult dedupCreateProp.template operator()(); dedupCreateProp.template operator()(); // dedupCreateProp.template operator()(); + dedupCreateProp.template operator()(); } // write out results @@ -3528,6 +3746,7 @@ auto CAssetConverter::reserve(const SInputs& inputs) -> SReserveResult ); }; // The order these are called is paramount, the Higher Level User needs to die to let go of dependants and make our Garbage Collection work + pruneStaging.template operator()(); // pruneStaging.template operator()(); pruneStaging.template operator()(); pruneStaging.template operator()(); @@ -3619,6 +3838,11 @@ auto CAssetConverter::reserve(const SInputs& inputs) -> SReserveResult return retval; } + +// Maps GPU Object back to the output array index +template +using reverse_map_t = core::unordered_map::video_t*,uint32_t>; + // ISemaphore::future_t CAssetConverter::convert_impl(SReserveResult& reservations, SConvertParams& params) { @@ -3660,16 +3884,20 @@ ISemaphore::future_t CAssetConverter::convert_impl(SReserveResul }; // wipe gpu item in staging cache (this may drop it as well if it was made for only a root asset == no users) - core::unordered_map outputReverseMap; - core::for_each_in_tuple(reservations.m_gpuObjects,[&outputReverseMap](const auto& gpuObjects)->void + core::tuple_transform_t outputReverseMaps; + core::for_each_in_tuple(reservations.m_gpuObjects,[&outputReverseMaps](const auto& gpuObjects)->void { uint32_t i = 0; for (const auto& gpuObj : gpuObjects) - outputReverseMap[gpuObj.value.get()] = i++; + { + const auto* ptr = gpuObj.value.get(); + std::get>(outputReverseMaps)[ptr] = i++; + } } ); - auto markFailure = [&reservations,&outputReverseMap,logger](const char* message, smart_refctd_ptr* canonical, typename SReserveResult::staging_cache_t::mapped_type* cacheNode)->void + auto markFailure = [&reservations,&outputReverseMaps,logger](const char* message, smart_refctd_ptr* canonical, typename SReserveResult::staging_cache_t::mapped_type* cacheNode)->void { + auto& outputReverseMap = std::get>(outputReverseMaps); // wipe the smart pointer to the canonical, make sure we release that memory ASAP if no other user is around *canonical = nullptr; // also drop the smart pointer from the output array so failures release memory quickly @@ -3680,7 +3908,14 @@ ISemaphore::future_t CAssetConverter::convert_impl(SReserveResul resultOutput[foundIx->second].value = nullptr; outputReverseMap.erase(foundIx); } - logger.log("%s failed for \"%s\"",system::ILogger::ELL_ERROR,message,cacheNode->gpuRef->getObjectDebugName()); + const char* name = "[Debug Name Unknown because not descended from IBackendObject]"; + if constexpr (std::is_base_of_v::video_t>) + name = cacheNode->gpuRef->getObjectDebugName(); + else + { + // TODO: get name from hash in cacheNode->cacheKey + } + logger.log("%s failed for \"%s\"",system::ILogger::ELL_ERROR,message,name); // drop smart pointer cacheNode->gpuRef = nullptr; }; @@ -5233,6 +5468,7 @@ ISemaphore::future_t CAssetConverter::convert_impl(SReserveResul if (const auto ix=compactedOwnershipReleaseIndices[i]; ixgetCreationParams().bufferRange; // swap out the conversion result + auto& outputReverseMap = std::get>(outputReverseMaps); const auto foundIx = outputReverseMap.find(srcAS); if (foundIx!=outputReverseMap.end()) { @@ -5385,6 +5621,27 @@ ISemaphore::future_t CAssetConverter::convert_impl(SReserveResul } } } + if constexpr (std::is_same_v) + { + depsMissing = missingDependent.template operator()(pGpuObj->getPositionView().src.buffer.get()); + auto checkView = [&](const IGPUPolygonGeometry::SDataView& view) -> void + { + if (depsMissing || !view) + return; + depsMissing = missingDependent.template operator()(view.src.buffer.get()); + }; + if (const auto* view=pGpuObj->getJointOBBView(); view) + checkView(*view); + checkView(pGpuObj->getIndexView()); + checkView(pGpuObj->getNormalView()); + for (const auto& entry : pGpuObj->getJointWeightViews()) + { + checkView(entry.indices); + checkView(entry.weights); + } + for (const auto& entry : pGpuObj->getAuxAttributeViews()) + checkView(entry); + } if (depsMissing) { smart_refctd_ptr dummy; @@ -5400,7 +5657,8 @@ ISemaphore::future_t CAssetConverter::convert_impl(SReserveResul checkDependents.template operator()(); checkDependents.template operator()(); checkDependents.template operator()(); -// mergeCache.template operator()(); +// checkDependents.template operator()(); + checkDependents.template operator()(); // overwrite the compacted TLASes in Descriptor Sets if (auto& tlasRewriteSet=reservations.m_potentialTLASRewrites; !tlasRewriteSet.empty()) { diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 1761ea9f07..57f66ad44b 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -2,5 +2,7 @@ add_subdirectory(nsc) add_subdirectory(xxHash256) if(NBL_BUILD_IMGUI) - add_subdirectory(nite) -endif() \ No newline at end of file + add_subdirectory(nite EXCLUDE_FROM_ALL) +endif() + +NBL_ADJUST_FOLDERS(tools) \ No newline at end of file