diff --git a/ci b/ci index 043f8a0aa0..8c3e98c651 160000 --- a/ci +++ b/ci @@ -1 +1 @@ -Subproject commit 043f8a0aa074f134b3230d56f27ed7d0c645f6b3 +Subproject commit 8c3e98c651e55a9e0e9c6121d055d483b2f90618 diff --git a/cmake/common.cmake b/cmake/common.cmake index 16ea1aee06..4ee0e705ca 100755 --- a/cmake/common.cmake +++ b/cmake/common.cmake @@ -350,13 +350,6 @@ function(nbl_install_media _FILE) nbl_install_lib_spec("${_FILE}" "") endfunction() -function(nbl_install_builtin_resources _TARGET_) - get_target_property(_BUILTIN_RESOURCES_INCLUDE_SEARCH_DIRECTORY_ ${_TARGET_} BUILTIN_RESOURCES_INCLUDE_SEARCH_DIRECTORY) - get_target_property(_BUILTIN_RESOURCES_HEADERS_ ${_TARGET_} BUILTIN_RESOURCES_HEADERS) - - nbl_install_headers_spec("${_BUILTIN_RESOURCES_HEADERS_}" "${_BUILTIN_RESOURCES_INCLUDE_SEARCH_DIRECTORY_}") -endfunction() - function(NBL_TEST_MODULE_INSTALL_FILE _NBL_FILEPATH_) file(RELATIVE_PATH _NBL_REL_INSTALL_DEST_ "${NBL_ROOT_PATH}" "${_NBL_FILEPATH_}") cmake_path(GET _NBL_REL_INSTALL_DEST_ PARENT_PATH _NBL_REL_INSTALL_DEST_) @@ -1184,12 +1177,13 @@ struct DeviceConfigCaps -enable-16bit-types -Zpr -spirv + -Wno-local-type-template-args -fspv-target-env=vulkan1.3 -Wshadow -Wconversion $<$:-O0> $<$:-O3> - $<$:-O3> + $<$:-O3> ) if(NSC_DEBUG_EDIF_FILE_BIT) @@ -1484,42 +1478,41 @@ namespace @IMPL_NAMESPACE@ { file(WRITE "${CONFIG_FILE}" "${CONFIG_CONTENT}") # generate keys and commands for compiling shaders - foreach(BUILD_CONFIGURATION ${CMAKE_CONFIGURATION_TYPES}) - set(FINAL_KEY_REL_PATH "${BUILD_CONFIGURATION}/${FINAL_KEY}") - set(TARGET_OUTPUT "${IMPL_BINARY_DIR}/${FINAL_KEY_REL_PATH}") - - set(NBL_NSC_COMPILE_COMMAND - "$" - -Fc "${TARGET_OUTPUT}" - ${COMPILE_OPTIONS} ${REQUIRED_OPTIONS} ${IMPL_COMMON_OPTIONS} - "${CONFIG_FILE}" - ) - - add_custom_command(OUTPUT "${TARGET_OUTPUT}" - COMMAND ${NBL_NSC_COMPILE_COMMAND} - DEPENDS ${DEPENDS_ON} - COMMENT "Creating \"${TARGET_OUTPUT}\"" - VERBATIM - COMMAND_EXPAND_LISTS - ) - - set(HEADER_ONLY_LIKE "${CONFIG_FILE}" "${TARGET_INPUT}" "${TARGET_OUTPUT}") - target_sources(${IMPL_TARGET} PRIVATE ${HEADER_ONLY_LIKE}) - - set_source_files_properties(${HEADER_ONLY_LIKE} PROPERTIES - HEADER_FILE_ONLY ON - VS_TOOL_OVERRIDE None - ) - - set_source_files_properties("${TARGET_OUTPUT}" PROPERTIES - NBL_SPIRV_REGISTERED_INPUT "${TARGET_INPUT}" - NBL_SPIRV_PERMUTATION_CONFIG "${CONFIG_FILE}" - NBL_SPIRV_BINARY_DIR "${IMPL_BINARY_DIR}" - NBL_SPIRV_ACCESS_KEY "${FINAL_KEY_REL_PATH}" - ) - - set_property(TARGET ${IMPL_TARGET} APPEND PROPERTY NBL_SPIRV_OUTPUTS "${TARGET_OUTPUT}") - endforeach() + set(FINAL_KEY_REL_PATH "$/${FINAL_KEY}") + set(TARGET_OUTPUT "${IMPL_BINARY_DIR}/${FINAL_KEY_REL_PATH}") + + set(NBL_NSC_COMPILE_COMMAND + "$" + -Fc "${TARGET_OUTPUT}" + ${COMPILE_OPTIONS} ${REQUIRED_OPTIONS} ${IMPL_COMMON_OPTIONS} + "${CONFIG_FILE}" + ) + + add_custom_command(OUTPUT "${TARGET_OUTPUT}" + COMMAND ${NBL_NSC_COMPILE_COMMAND} + DEPENDS ${DEPENDS_ON} + COMMENT "Creating \"${TARGET_OUTPUT}\"" + VERBATIM + COMMAND_EXPAND_LISTS + ) + set_source_files_properties("${TARGET_OUTPUT}" PROPERTIES GENERATED TRUE) + + set(HEADER_ONLY_LIKE "${CONFIG_FILE}" "${TARGET_INPUT}" "${TARGET_OUTPUT}") + target_sources(${IMPL_TARGET} PRIVATE ${HEADER_ONLY_LIKE}) + + set_source_files_properties(${HEADER_ONLY_LIKE} PROPERTIES + HEADER_FILE_ONLY ON + VS_TOOL_OVERRIDE None + ) + + set_source_files_properties("${TARGET_OUTPUT}" PROPERTIES + NBL_SPIRV_REGISTERED_INPUT "${TARGET_INPUT}" + NBL_SPIRV_PERMUTATION_CONFIG "${CONFIG_FILE}" + NBL_SPIRV_BINARY_DIR "${IMPL_BINARY_DIR}" + NBL_SPIRV_ACCESS_KEY "${FINAL_KEY_REL_PATH}" + ) + + set_property(TARGET ${IMPL_TARGET} APPEND PROPERTY NBL_SPIRV_OUTPUTS "${TARGET_OUTPUT}") return() endif() diff --git a/examples_tests b/examples_tests index 860961034d..81449834f4 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 860961034dd57899fbe1ffc48267e3b2df775a18 +Subproject commit 81449834f4efd4fe649f7dc706ab202ca92c45f3 diff --git a/include/nbl/asset/asset.h b/include/nbl/asset/asset.h index fe70e81646..f08844a182 100644 --- a/include/nbl/asset/asset.h +++ b/include/nbl/asset/asset.h @@ -67,6 +67,6 @@ #include "nbl/asset/metadata/CMTLMetadata.h" #include "nbl/asset/metadata/CPLYMetadata.h" #include "nbl/asset/metadata/CSTLMetadata.h" -//#include "nbl/asset/metadata/CIESProfileMetadata.h" +#include "nbl/asset/metadata/CIESProfileMetadata.h" #endif diff --git a/include/nbl/asset/utils/CGeometryCreator.h b/include/nbl/asset/utils/CGeometryCreator.h index 5c669b9510..290c81b239 100644 --- a/include/nbl/asset/utils/CGeometryCreator.h +++ b/include/nbl/asset/utils/CGeometryCreator.h @@ -115,9 +115,15 @@ class NBL_API2 CGeometryCreator final : public core::IReferenceCounted \param subdivision Specifies subdivision level of the icosphere. \param smooth Specifies whether vertecies should be built for smooth or flat shading. */ - core::smart_refctd_ptr createIcoSphere(float radius=1.f, uint32_t subdivision=1, bool smooth=false) const; + //! Create a grid geometry + /** + No vertex buffer, only index in triangle strip topology without reset, "snake" with degenerates + \param "resolution" Specifies resolution of grid + */ + core::smart_refctd_ptr createGrid(const hlsl::uint16_t2 resolution = { 128u, 128u }) const; + private: SCreationParams m_params; }; diff --git a/include/nbl/builtin/glsl/ies/functions.glsl b/include/nbl/builtin/glsl/ies/functions.glsl index 70f5165f98..54c266d114 100644 --- a/include/nbl/builtin/glsl/ies/functions.glsl +++ b/include/nbl/builtin/glsl/ies/functions.glsl @@ -6,25 +6,33 @@ #include -// TODO: implement proper mirroing -// MIRROR_180_BITS = 0b001, Last Angle is 180, so map V slightly differently -// MIRROR_90_BITS = 0b010, Last Angle is 90, so map both U and V slightly differently -// ISOTROPIC_BITS = 0b011, texture to sample is Nx1, pretend v=middle always -// FULL_THETA_BIT = 0b100, handle extended domain and rotate by 45 degrees for anisotropic - -vec2 nbl_glsl_IES_convert_dir_to_uv(vec3 dir) { - float sum = dot(vec3(1.0f), abs(dir)); +// TODO: when rewriting to HLSL this is not IES namespace or folder, this should be octahedral mapping sitting somewhere where the spherical/polar sits +// NOTE: I changed it to return NDC [-1,1]^2 instead of UV coords [0,1]^2 +vec2 nbl_glsl_TODOnamespace_octahedral_mapping(vec3 dir) +{ + float sum = dot(vec3(1.0f), abs(dir)); vec3 s = dir / sum; - if(s.z < 0.0f) { - s.xy = sign(s.xy) * (1.0f - abs(s.yx)); + if(s.z < 0.0f) + { + const uvec2 flipSignMask = floatBitsToUint(s.xy)&0x80000000u; + s.xy = uintBitsToFloat(floatBitsToUint(1.0f - abs(s.yx))^flipSignMask); } - return s.xy * 0.5f + 0.5f; + return s.xy; } -// vec2 nbl_glsl_IES_convert_dir_to_uv(vec3 dir) { -// return vec2((atan(dir.x, dir.y) + nbl_glsl_PI) / (2.0*nbl_glsl_PI), acos(dir.z) / nbl_glsl_PI); -// } +// TODO: implement proper mirroing +// MIRROR_180_BITS = 0b001, Last Angle is 180, so map V with MIRROR and corner sampling off +// MIRROR_90_BITS = 0b010, Last Angle is 90, so map both U and V with MIRROR and corner sampling off +// ISOTROPIC_BITS = 0b011, texture to sample is Nx1, pretend v=middle always , and make u REPEAT or CLAMP_TO_BORDER +// FULL_THETA_BIT = 0b100, handle truncated domain and rotate by 45 degrees for anisotropic +// (certain combos wont work like 90 degree 2 symmetry domain & half theta), it really needs to be an 8 case label thing explicitly enumerated +vec2 nbl_glsl_IES_convert_dir_to_uv(vec3 dir, vec2 halfMinusHalfPixel) +{ + // halfMinusHalfPixel = 0.5-0.5/texSize + // believe it or not, cornerSampled(NDC*0.5+0.5) = NDC*0.5*(1-1/texSize)+0.5 + return nbl_glsl_TODOnamespace_octahedral_mapping(dir)*halfMinusHalfPixel+0.5; +} #endif \ No newline at end of file diff --git a/include/nbl/builtin/glsl/material_compiler/common.glsl b/include/nbl/builtin/glsl/material_compiler/common.glsl index da67b12cbf..c0a52a4f0d 100644 --- a/include/nbl/builtin/glsl/material_compiler/common.glsl +++ b/include/nbl/builtin/glsl/material_compiler/common.glsl @@ -215,6 +215,7 @@ bool nbl_glsl_MC_op_isDelta(in uint op) #ifdef TEX_PREFETCH_STREAM #include #endif +// TODO: once rewritten to HLSL, shall use new API #include // OptiX likes this one better @@ -601,7 +602,8 @@ vec3 nbl_glsl_MC_oriented_material_t_getEmissive(in nbl_glsl_MC_oriented_materia if ((floatBitsToInt(emitter.orientation[0])&1u) != 1u) { right *= -1; } - return emissive * nbl_glsl_vTextureGrad(emitter.emissionProfile, nbl_glsl_IES_convert_dir_to_uv(mat3(right, up, view)*dir), mat2(0.0)).r; + vec2 halfMinusHalfPixel = vec2(0.5)-vec2(0.5)/vec2(nbl_glsl_unpackSize(emitter.emissionProfile)); + return emissive * nbl_glsl_vTextureGrad(emitter.emissionProfile, nbl_glsl_IES_convert_dir_to_uv(mat3(right, up, view)*dir,halfMinusHalfPixel), mat2(0.0)).r; } #endif return emissive; @@ -1498,4 +1500,4 @@ nbl_glsl_MC_quot_pdf_aov_t nbl_glsl_MC_runGenerateAndRemainderStream( } #endif //GEN_CHOICE_STREAM -#endif \ No newline at end of file +#endif diff --git a/include/nbl/builtin/hlsl/ext/FullScreenTriangle/default.vert.hlsl b/include/nbl/builtin/hlsl/ext/FullScreenTriangle/default.vert.hlsl index a48d9b4623..7a2aef1cbf 100644 --- a/include/nbl/builtin/hlsl/ext/FullScreenTriangle/default.vert.hlsl +++ b/include/nbl/builtin/hlsl/ext/FullScreenTriangle/default.vert.hlsl @@ -33,4 +33,5 @@ SVertexAttributes main() SVertexAttributes retval; retval.uv = tc[gl_VertexIndex()]; return retval; -} \ No newline at end of file +} + diff --git a/include/nbl/builtin/hlsl/ies/profile.hlsl b/include/nbl/builtin/hlsl/ies/profile.hlsl new file mode 100644 index 0000000000..a85141aebd --- /dev/null +++ b/include/nbl/builtin/hlsl/ies/profile.hlsl @@ -0,0 +1,117 @@ +// 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_IES_PROFILE_INCLUDED_ +#define _NBL_BUILTIN_HLSL_IES_PROFILE_INCLUDED_ + +#include "nbl/builtin/hlsl/cpp_compat.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace ies +{ + +struct ProfileProperties +{ + //! max 16K resolution + NBL_CONSTEXPR_STATIC_INLINE uint32_t CDC_MAX_TEXTURE_WIDTH = 15360u; + NBL_CONSTEXPR_STATIC_INLINE uint32_t CDC_MAX_TEXTURE_HEIGHT = 8640u; + + // TODO: This constraint is hack because the mitsuba loader and its material compiler use Virtual Texturing, and there's some bug with IES not sampling sub 128x128 mip levels + // don't want to spend time to fix this since we'll be using descriptor indexing for the next iteration + NBL_CONSTEXPR_STATIC_INLINE uint32_t CDC_MIN_TEXTURE_WIDTH = 128u; + NBL_CONSTEXPR_STATIC_INLINE uint32_t CDC_MIN_TEXTURE_HEIGHT = 128u; + + NBL_CONSTEXPR_STATIC_INLINE uint32_t CDC_DEFAULT_TEXTURE_WIDTH = 1024u; + NBL_CONSTEXPR_STATIC_INLINE uint32_t CDC_DEFAULT_TEXTURE_HEIGHT = 1024u; + + NBL_CONSTEXPR_STATIC_INLINE float32_t MAX_VANGLE = 180.f; + NBL_CONSTEXPR_STATIC_INLINE float32_t MAX_HANGLE = 360.f; + + // TODO: could change to uint8_t once we get implemented + // https://github.com/microsoft/hlsl-specs/pull/538 + using packed_flags_t = uint16_t; + + NBL_CONSTEXPR_STATIC_INLINE packed_flags_t VERSION_BITS = 2u; + NBL_CONSTEXPR_STATIC_INLINE packed_flags_t TYPE_BITS = 2u; + NBL_CONSTEXPR_STATIC_INLINE packed_flags_t SYMM_BITS = 3u; + NBL_CONSTEXPR_STATIC_INLINE packed_flags_t VERSION_MASK = (packed_flags_t(1u) << VERSION_BITS) - packed_flags_t(1u); + NBL_CONSTEXPR_STATIC_INLINE packed_flags_t TYPE_MASK = (packed_flags_t(1u) << TYPE_BITS) - packed_flags_t(1u); + NBL_CONSTEXPR_STATIC_INLINE packed_flags_t SYMM_MASK = (packed_flags_t(1u) << SYMM_BITS) - packed_flags_t(1u); + + enum Version : packed_flags_t + { + V_1995, + V_2002, + V_SIZE + }; + + enum PhotometricType : packed_flags_t + { + TYPE_NONE, + TYPE_C, + TYPE_B, + TYPE_A + }; + + enum LuminairePlanesSymmetry : packed_flags_t + { + ISOTROPIC, //! Only one horizontal angle present and a luminaire is assumed to be laterally axial symmetric + QUAD_SYMETRIC, //! The luminaire is assumed to be symmetric in each quadrant + HALF_SYMETRIC, //! The luminaire is assumed to be symmetric about the 0 to 180 degree plane + OTHER_HALF_SYMMETRIC, //! HALF_SYMETRIC case for legacy V_1995 version where horizontal angles are in range [90, 270], in that case the parser patches horizontal angles to be HALF_SYMETRIC + NO_LATERAL_SYMMET //! The luminaire is assumed to exhibit no lateral symmet + }; + + Version getVersion() NBL_CONST_MEMBER_FUNC + { + return (Version)( packed & VERSION_MASK ); + } + + PhotometricType getType() NBL_CONST_MEMBER_FUNC + { + const packed_flags_t shift = VERSION_BITS; + return (PhotometricType)((packed >> shift) & TYPE_MASK); + } + + LuminairePlanesSymmetry getSymmetry() NBL_CONST_MEMBER_FUNC + { + const packed_flags_t shift = VERSION_BITS + TYPE_BITS; + return (LuminairePlanesSymmetry)((packed >> shift) & SYMM_MASK); + } + + void setVersion(Version v) + { + packed_flags_t vBits = (packed_flags_t)(v) & VERSION_MASK; + packed = (packed & ~VERSION_MASK) | vBits; + } + + void setType(PhotometricType t) + { + const packed_flags_t shift = VERSION_BITS; + packed_flags_t tBits = ((packed_flags_t)(t) & TYPE_MASK) << shift; + packed = (packed & ~(TYPE_MASK << shift)) | tBits; + } + + void setSymmetry(LuminairePlanesSymmetry s) + { + const packed_flags_t shift = VERSION_BITS + TYPE_BITS; + packed_flags_t sBits = ((packed_flags_t)(s) & SYMM_MASK) << shift; + packed = (packed & ~(SYMM_MASK << shift)) | sBits; + } + + float32_t maxCandelaValue; //! Max candela sample value + float32_t totalEmissionIntegral; //! Total emitted intensity (integral over full angular domain) + float32_t fullDomainAvgEmission; //! Mean intensity over full angular domain (including I == 0) + float32_t avgEmmision; //! Mean intensity over emitting solid angle (I > 0) + packed_flags_t packed; //! Packed version, type and symmetry flags +}; + +} +} +} + +#endif // _NBL_BUILTIN_HLSL_IES_PROFILE_INCLUDED_ diff --git a/include/nbl/builtin/hlsl/ies/sampler.hlsl b/include/nbl/builtin/hlsl/ies/sampler.hlsl new file mode 100644 index 0000000000..41f273e82c --- /dev/null +++ b/include/nbl/builtin/hlsl/ies/sampler.hlsl @@ -0,0 +1,170 @@ +// 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_IES_SAMPLER_INCLUDED_ +#define _NBL_BUILTIN_HLSL_IES_SAMPLER_INCLUDED_ + +#include "nbl/builtin/hlsl/cpp_compat.hlsl" +#include "nbl/builtin/hlsl/math/polar.hlsl" +#include "nbl/builtin/hlsl/math/octahedral.hlsl" +#include "nbl/builtin/hlsl/concepts.hlsl" +#include "nbl/builtin/hlsl/ies/profile.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace ies +{ +namespace concepts +{ +#define NBL_CONCEPT_NAME IESAccessor +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (accessor_t) +#define NBL_CONCEPT_PARAM_0 (accessor, accessor_t) +NBL_CONCEPT_BEGIN(1) +#define accessor NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define req_key_t uint32_t +#define req_key_t2 uint32_t2 +#define req_value_t float32_t +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(accessor_t::key_t)) + ((NBL_CONCEPT_REQ_TYPE)(accessor_t::key_t2)) + ((NBL_CONCEPT_REQ_TYPE)(accessor_t::value_t)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((req_key_t(0)), is_same_v, typename accessor_t::key_t)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((req_key_t2(0, 0)), is_same_v, typename accessor_t::key_t2)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((req_value_t(0)), is_same_v, typename accessor_t::value_t)) + + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((accessor.vAnglesCount()), is_same_v, req_key_t)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((accessor.hAnglesCount()), is_same_v, req_key_t)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((accessor.getProperties()), is_same_v, ProfileProperties)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((accessor.template vAngle((req_key_t)0)), is_same_v, req_value_t)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((accessor.template hAngle((req_key_t)0)), is_same_v, req_value_t)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((accessor.template value((req_key_t2)0)), is_same_v, req_value_t)) +); +#undef accessor +#undef req_key_t +#undef req_key_t2 +#undef req_value_t +#include + +template +NBL_BOOL_CONCEPT IsIESAccessor = IESAccessor; +} + +template) +struct CandelaSampler +{ + using accessor_t = Accessor; + using value_t = typename accessor_t::value_t; + using symmetry_t = ProfileProperties::LuminairePlanesSymmetry; + using polar_t = math::Polar; + using octahedral_t = math::OctahedralTransform; + + static value_t sample(NBL_CONST_REF_ARG(accessor_t) accessor, NBL_CONST_REF_ARG(math::Polar) polar) + { + // TODO: DXC seems to have a bug and cannot use symmetry_t directly with == operator https://godbolt.devsh.eu/z/P9Kc5x + const ProfileProperties::LuminairePlanesSymmetry symmetry = accessor.getProperties().getSymmetry(); + const float32_t vAngle = degrees(polar.theta); + const float32_t hAngle = degrees(wrapPhi(polar.phi, symmetry)); + + const float32_t vABack = accessor.vAngle(accessor.vAnglesCount() - 1u); + if (vAngle > vABack) + return 0.f; + + const uint32_t j0 = getVLB(accessor, vAngle); + const uint32_t j1 = getVUB(accessor, vAngle); + const uint32_t i0 = (symmetry == ProfileProperties::LuminairePlanesSymmetry::ISOTROPIC) ? 0u : getHLB(accessor, hAngle); + const uint32_t i1 = (symmetry == ProfileProperties::LuminairePlanesSymmetry::ISOTROPIC) ? 0u : getHUB(accessor, hAngle); + + const float32_t uReciprocal = ((i1 == i0) ? 1.f : 1.f / (accessor.hAngle(i1) - accessor.hAngle(i0))); + const float32_t vReciprocal = ((j1 == j0) ? 1.f : 1.f / (accessor.vAngle(j1) - accessor.vAngle(j0))); + + const float32_t u = ((hAngle - accessor.hAngle(i0)) * uReciprocal); + const float32_t v = ((vAngle - accessor.vAngle(j0)) * vReciprocal); + + const float32_t s0 = (accessor.value(uint32_t2(i0, j0)) * (1.f - v) + accessor.value(uint32_t2(i0, j1)) * v); + const float32_t s1 = (accessor.value(uint32_t2(i1, j0)) * (1.f - v) + accessor.value(uint32_t2(i1, j1)) * v); + + return s0 * (1.f - u) + s1 * u; + } + + static value_t sample(NBL_CONST_REF_ARG(accessor_t) accessor, NBL_CONST_REF_ARG(float32_t2) uv) + { + const float32_t3 dir = octahedral_t::uvToDir(uv); + const polar_t polar = polar_t::createFromCartesian(dir); + return sample(accessor, polar); + } + + static float32_t wrapPhi(const float32_t phi, const symmetry_t symmetry) + { + switch (symmetry) + { + case symmetry_t::ISOTROPIC: //! axial symmetry + return 0.0f; + case symmetry_t::QUAD_SYMETRIC: //! phi MIRROR_REPEAT wrap onto [0, 90] degrees range + { + NBL_CONSTEXPR float32_t M_HALF_PI = numbers::pi * 0.5f; + float32_t wrapPhi = abs(phi); //! first MIRROR + if (wrapPhi > M_HALF_PI) //! then REPEAT + wrapPhi = hlsl::clamp(M_HALF_PI - (wrapPhi - M_HALF_PI), 0.f, M_HALF_PI); + return wrapPhi; //! eg. maps (in degrees) 91,269,271 -> 89 and 179,181,359 -> 1 + } + case symmetry_t::HALF_SYMETRIC: //! phi MIRROR wrap onto [0, 180] degrees range + case symmetry_t::OTHER_HALF_SYMMETRIC: //! eg. maps (in degress) 181 -> 179 or 359 -> 1 + return abs(phi); + case symmetry_t::NO_LATERAL_SYMMET: //! plot onto whole (in degress) [0, 360] range + { + NBL_CONSTEXPR float32_t M_TWICE_PI = numbers::pi *2.f; + return (phi < 0.f) ? (phi + M_TWICE_PI) : phi; + } + } + return 69.f; + } + + struct impl_t + { + static uint32_t getVUB(NBL_CONST_REF_ARG(accessor_t) accessor, const float32_t angle) + { + for (uint32_t i = 0u; i < accessor.vAnglesCount(); ++i) + if (accessor.vAngle(i) > angle) + return i; + return accessor.vAnglesCount(); + } + + static uint32_t getHUB(NBL_CONST_REF_ARG(accessor_t) accessor, const float32_t angle) + { + for (uint32_t i = 0u; i < accessor.hAnglesCount(); ++i) + if (accessor.hAngle(i) > angle) + return i; + return accessor.hAnglesCount(); + } + }; + + static uint32_t getVLB(NBL_CONST_REF_ARG(accessor_t) accessor, const float32_t angle) + { + return (uint32_t)hlsl::max((int64_t)impl_t::getVUB(accessor, angle) - 1ll, 0ll); + } + + static uint32_t getHLB(NBL_CONST_REF_ARG(accessor_t) accessor, const float32_t angle) + { + return (uint32_t)hlsl::max((int64_t)impl_t::getHUB(accessor, angle) - 1ll, 0ll); + } + + static uint32_t getVUB(NBL_CONST_REF_ARG(accessor_t) accessor, const float32_t angle) + { + return (uint32_t)hlsl::min((int64_t)impl_t::getVUB(accessor, angle), (int64_t)(accessor.vAnglesCount() - 1u)); + } + + static uint32_t getHUB(NBL_CONST_REF_ARG(accessor_t) accessor, const float32_t angle) + { + return (uint32_t)hlsl::min((int64_t)impl_t::getHUB(accessor, angle), (int64_t)(accessor.hAnglesCount() - 1u)); + } +}; + +} +} +} + +#endif // _NBL_BUILTIN_HLSL_IES_SAMPLER_INCLUDED_ diff --git a/include/nbl/builtin/hlsl/ies/texture.hlsl b/include/nbl/builtin/hlsl/ies/texture.hlsl new file mode 100644 index 0000000000..4ea04755df --- /dev/null +++ b/include/nbl/builtin/hlsl/ies/texture.hlsl @@ -0,0 +1,101 @@ +// 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_IES_TEXTURE_INCLUDED_ +#define _NBL_BUILTIN_HLSL_IES_TEXTURE_INCLUDED_ + +#include "nbl/builtin/hlsl/ies/sampler.hlsl" +#include "nbl/builtin/hlsl/bda/struct_declare.hlsl" + +namespace nbl +{ +namespace hlsl +{ + +// TODO(?): should be in nbl::hlsl::ies (or in the Texutre struct) but I get +// error GA3909C62: class template specialization of 'member_count' not in a namespace enclosing 'bda' +// which I don't want to deal with rn to not (eventually) break stuff + +struct IESTextureInfo; +NBL_HLSL_DEFINE_STRUCT((IESTextureInfo), + ((inv, float32_t2)) + ((flatten, float32_t)) + ((maxValueRecip, float32_t)) + ((flattenTarget, float32_t)) + ((domainLo, float32_t)) + ((domainHi, float32_t)) + ((fullDomainFlatten, uint16_t)) // bool +); + +namespace ies +{ + +template) +struct Texture +{ + using accessor_t = Accessor; + using value_t = typename accessor_t::value_t; + using sampler_t = CandelaSampler; + using polar_t = math::Polar; + using octahedral_t = math::OctahedralTransform; + using SInfo = nbl::hlsl::IESTextureInfo; + + static inline SInfo createInfo(NBL_CONST_REF_ARG(accessor_t) accessor, NBL_CONST_REF_ARG(uint32_t2) size, float32_t flatten, bool fullDomainFlatten) + { + SInfo retval; + const ProfileProperties props = accessor.getProperties(); + + // There is one huge issue, the IES files love to give us values for degrees 0, 90, 180 an 360 + // So standard octahedral mapping won't work, because for above data points you need corner sampled images. + + retval.inv = float32_t2(1.f, 1.f) / float32_t2(size - 1u); + retval.flatten = flatten; + retval.maxValueRecip = 1.0f / props.maxCandelaValue; // Late Optimization TODO: Modify the Max Value for the UNORM texture to be the Max Value after flatten blending + retval.domainLo = radians(accessor.vAngle(0u)); + retval.domainHi = radians(accessor.vAngle(accessor.vAnglesCount() - 1u)); + retval.fullDomainFlatten = fullDomainFlatten; + + if(fullDomainFlatten) + retval.flattenTarget = props.fullDomainAvgEmission; + else + retval.flattenTarget = props.avgEmmision; + + return retval; + } + + static inline float32_t eval(NBL_CONST_REF_ARG(accessor_t) accessor, NBL_CONST_REF_ARG(SInfo) info, NBL_CONST_REF_ARG(float32_t2) uv) + { + // We don't currently support generating IES images that exploit symmetries or reduced domains, all are full octahederal mappings of a sphere. + // If we did, we'd rely on MIRROR and CLAMP samplers to do some of the work for us while handling the discontinuity due to corner sampling. + const float32_t3 dir = octahedral_t::uvToDir(uv); + const polar_t polar = polar_t::createFromCartesian(dir); + + sampler_t sampler; + const float32_t intensity = sampler.sample(accessor, polar); + + //! blend the IES texture with "flatten" + float32_t blendV = intensity * (1.f - info.flatten); + + const bool inDomain = (info.domainLo <= polar.theta) && (polar.theta <= info.domainHi); + + if ((info.fullDomainFlatten && inDomain) || intensity > 0.0f) + blendV += info.flattenTarget * info.flatten; + + blendV *= info.maxValueRecip; + + return blendV; + } + + static inline float32_t eval(NBL_CONST_REF_ARG(accessor_t) accessor, NBL_CONST_REF_ARG(SInfo) info, NBL_CONST_REF_ARG(uint32_t2) position) + { + const float32_t2 uv = float32_t2(position) * info.inv; + return eval(accessor, info, uv); + } +}; + +} +} +} + +#endif // _NBL_BUILTIN_HLSL_IES_TEXTURE_INCLUDED_ diff --git a/include/nbl/builtin/hlsl/math/octahedral.hlsl b/include/nbl/builtin/hlsl/math/octahedral.hlsl new file mode 100644 index 0000000000..45fe35b2d8 --- /dev/null +++ b/include/nbl/builtin/hlsl/math/octahedral.hlsl @@ -0,0 +1,75 @@ +// 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_MATH_OCTAHEDRAL_INCLUDED_ +#define _NBL_BUILTIN_HLSL_MATH_OCTAHEDRAL_INCLUDED_ + +#include "nbl/builtin/hlsl/cpp_compat.hlsl" +#include "nbl/builtin/hlsl/numbers.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace math +{ + +// Octahedral Transform, maps 3D direction vectors to 2D square and vice versa +template +struct OctahedralTransform +{ + using scalar_type = T; + using vector2_type = vector; + using vector3_type = vector; + + // F : [0, 1]^2 -> S^2 + static vector3_type uvToDir(NBL_CONST_REF_ARG(vector2_type) uv) + { + vector3_type p = vector3_type((uv * scalar_type(2) - scalar_type(1)), scalar_type(0)); + const scalar_type a_x = abs(p.x); const scalar_type a_y = abs(p.y); + + p.z = scalar_type(1) - a_x - a_y; + + if (p.z < scalar_type(0)) + { + p.x = (p.x < scalar_type(0) ? scalar_type(-1) : scalar_type(1)) * (scalar_type(1) - a_y); + p.y = (p.y < scalar_type(0) ? scalar_type(-1) : scalar_type(1)) * (scalar_type(1) - a_x); + } + + return hlsl::normalize(p); + } + + // F^-1 : S^2 -> [-1, 1]^2 + static vector2_type dirToNDC(NBL_CONST_REF_ARG(vector3_type) d) + { + vector3_type dir = hlsl::normalize(d); + const scalar_type sum = dot(vector3_type(scalar_type(1), scalar_type(1), scalar_type(1)), abs(dir)); + vector3_type s = dir / sum; + + if (s.z < scalar_type(0)) + { + s.x = (s.x < scalar_type(0) ? scalar_type(-1) : scalar_type(1)) * (scalar_type(1) - abs(s.y)); + s.y = (s.y < scalar_type(0) ? scalar_type(-1) : scalar_type(1)) * (scalar_type(1) - abs(s.x)); + } + + return s.xy; + } + + // transforms direction vector into UV for corner sampling + // dir in S^2, halfMinusHalfPixel in [0, 0.5)^2, + // where halfMinusHalfPixel = 0.5-0.5/texSize + // and texSize.x >= 1, texSize.y >= 1 + // NOTE/TODO: not best place to keep it here + static vector2_type toCornerSampledUV(NBL_CONST_REF_ARG(vector3_type) dir, NBL_CONST_REF_ARG(vector2_type) halfMinusHalfPixel) + { + // note: cornerSampled(NDC*0.5+0.5) = NDC*0.5*(1-1/texSize)+0.5 + return dirToNDC(dir) * halfMinusHalfPixel + scalar_type(0.5); + } +}; + +} +} +} + +#endif // _NBL_BUILTIN_HLSL_MATH_OCTAHEDRAL_INCLUDED_ diff --git a/include/nbl/builtin/hlsl/math/polar.hlsl b/include/nbl/builtin/hlsl/math/polar.hlsl index 7b30e3bb8f..01a95f61ef 100644 --- a/include/nbl/builtin/hlsl/math/polar.hlsl +++ b/include/nbl/builtin/hlsl/math/polar.hlsl @@ -14,27 +14,29 @@ namespace hlsl namespace math { -template +template struct Polar { using scalar_type = T; using vector2_type = vector; using vector3_type = vector; - // should be normalized - static Polar createFromCartesian(const vector3_type coords) + // input must be normalized + static Polar createFromCartesian(NBL_CONST_REF_ARG(vector3_type) dir) { Polar retval; - retval.theta = hlsl::acos(coords.z); - retval.phi = hlsl::atan2(coords.y, coords.x); + retval.theta = acos(dir.z); + retval.phi = atan2(dir.y, dir.x); return retval; } - static vector3_type ToCartesian(const scalar_type theta, const scalar_type phi) + static vector3_type ToCartesian(NBL_CONST_REF_ARG(scalar_type) theta, NBL_CONST_REF_ARG(scalar_type) phi) { - return vector(hlsl::cos(phi) * hlsl::cos(theta), - hlsl::sin(phi) * hlsl::cos(theta), - hlsl::sin(theta)); + return vector( + cos(phi) * cos(theta), + sin(phi) * cos(theta), + sin(theta) + ); } vector3_type getCartesian() @@ -42,8 +44,8 @@ struct Polar return ToCartesian(theta, phi); } - scalar_type theta; - scalar_type phi; + scalar_type theta; //! polar angle + scalar_type phi; //! azimuthal angle }; } diff --git a/include/nbl/builtin/hlsl/surface_transform.h b/include/nbl/builtin/hlsl/surface_transform.h index a681ecf0bb..0b93434fe0 100644 --- a/include/nbl/builtin/hlsl/surface_transform.h +++ b/include/nbl/builtin/hlsl/surface_transform.h @@ -174,11 +174,12 @@ inline float32_t2 applyToNDC(const FLAG_BITS transform, const float32_t2 ndc) template TwoColumns applyToDerivatives(const FLAG_BITS transform, TwoColumns dDx_dDy) { - using namespace glsl; // IN HLSL mode, C++ doens't need this to access `inverse` - return mul(inverse(transformMatrix(transform)),dDx_dDy); + return mul(inverse(transformMatrix(transform)),dDx_dDy); } } } } -#endif \ No newline at end of file + +#endif // _NBL_BUILTIN_HLSL_SURFACE_TRANSFORM_INCLUDED_ + diff --git a/include/nbl/ext/ImGui/ImGui.h b/include/nbl/ext/ImGui/ImGui.h index 5f3c1d5f08..16f080d102 100644 --- a/include/nbl/ext/ImGui/ImGui.h +++ b/include/nbl/ext/ImGui/ImGui.h @@ -99,6 +99,14 @@ class UI final : public core::IReferenceCounted //! optional, no cache used if not provided core::smart_refctd_ptr pipelineCache = nullptr; + + struct PrecompiledShaders + { + core::smart_refctd_ptr vertex = nullptr, fragment = nullptr; + }; + + //! optional, precompiled spirv shaders (experimental) + std::optional spirv = std::nullopt; }; //! parameters which may change every frame, used with the .update call to interact with ImGuiIO; we require a very *required* minimum - if you need to cover more IO options simply get the IO with ImGui::GetIO() to customize them (they all have default values you can change before calling the .update) diff --git a/include/nbl/ext/ImGui/builtin/hlsl/fragment.hlsl b/include/nbl/ext/ImGui/builtin/hlsl/fragment.hlsl index 26e2b461a3..a0e70dfebd 100644 --- a/include/nbl/ext/ImGui/builtin/hlsl/fragment.hlsl +++ b/include/nbl/ext/ImGui/builtin/hlsl/fragment.hlsl @@ -22,13 +22,11 @@ #error "NBL_SAMPLERS_COUNT must be defined!" #endif -#include "common.hlsl" +#include "pc.hlsl" #include "psinput.hlsl" using namespace nbl::ext::imgui; -[[vk::push_constant]] struct PushConstants pc; - // separable image samplers to handle textures we do descriptor-index [[vk::binding(NBL_TEXTURES_BINDING_IX, NBL_TEXTURES_SET_IX)]] Texture2D textures[NBL_TEXTURES_COUNT]; [[vk::binding(NBL_SAMPLER_STATES_BINDING_IX, NBL_SAMPLER_STATES_SET_IX)]] SamplerState samplerStates[NBL_SAMPLERS_COUNT]; @@ -39,6 +37,7 @@ using namespace nbl::ext::imgui; to request per object data with BDA */ +[shader("pixel")] float4 PSMain(PSInput input) : SV_Target0 { // BDA for requesting object data diff --git a/include/nbl/ext/ImGui/builtin/hlsl/pc.hlsl b/include/nbl/ext/ImGui/builtin/hlsl/pc.hlsl new file mode 100644 index 0000000000..103ce5b6c1 --- /dev/null +++ b/include/nbl/ext/ImGui/builtin/hlsl/pc.hlsl @@ -0,0 +1,8 @@ +#ifndef _NBL_IMGUI_EXT_PC_HLSL_ +#define _NBL_IMGUI_EXT_PC_HLSL_ + +// TODO: have only unified.hlsl uber shader and common.hlsl then update imgui cpp files, doing a quick workaround for my prebuilds +#include "common.hlsl" +[[vk::push_constant]] struct nbl::ext::imgui::PushConstants pc; + +#endif // _NBL_IMGUI_EXT_PC_HLSL_ diff --git a/include/nbl/ext/ImGui/builtin/hlsl/vertex.hlsl b/include/nbl/ext/ImGui/builtin/hlsl/vertex.hlsl index 1651060c58..ef4582146e 100644 --- a/include/nbl/ext/ImGui/builtin/hlsl/vertex.hlsl +++ b/include/nbl/ext/ImGui/builtin/hlsl/vertex.hlsl @@ -1,10 +1,8 @@ -#include "common.hlsl" +#include "pc.hlsl" #include "psinput.hlsl" using namespace nbl::ext::imgui; -[[vk::push_constant]] struct PushConstants pc; - struct VSInput { [[vk::location(0)]] float2 position : POSITION; @@ -18,6 +16,7 @@ struct VSInput to request per object data with BDA */ +[shader("vertex")] PSInput VSMain(VSInput input, uint drawID : SV_InstanceID) { PSInput output; diff --git a/include/shadertoolsconfig.json b/include/shadertoolsconfig.json new file mode 100644 index 0000000000..4c16b1f743 --- /dev/null +++ b/include/shadertoolsconfig.json @@ -0,0 +1,8 @@ +{ + "root": true, + "hlsl.preprocessorDefinitions": { + }, + "hlsl.additionalIncludeDirectories": [ + "." + ] +} \ No newline at end of file diff --git a/src/nbl/CMakeLists.txt b/src/nbl/CMakeLists.txt index 76e046848c..895488cf45 100644 --- a/src/nbl/CMakeLists.txt +++ b/src/nbl/CMakeLists.txt @@ -224,6 +224,11 @@ set(NBL_ASSET_SOURCES asset/interchange/CImageWriterTGA.cpp asset/interchange/CImageWriterOpenEXR.cpp # TODO: Nahim asset/interchange/CGLIWriter.cpp + +# IES profile loaders + asset/interchange/CIESProfileLoader.cpp + asset/utils/CIESProfileParser.cpp + asset/utils/CIESProfile.cpp ) set(NBL_VIDEO_SOURCES # Utilities @@ -689,6 +694,7 @@ if(NBL_EMBED_BUILTIN_RESOURCES) target_compile_definitions(${NBL_TARGET} PUBLIC "$") # workaround because must use Nabla headers without linking Nabla to itself.. target_include_directories(${NBL_TARGET} PUBLIC "$") target_include_directories(Nabla PRIVATE "${_INTERNAL_BR_OUTPUT_INCLUDE_SEARCH_DIRECTORY_}") + target_include_directories(Nabla INTERFACE "$") endforeach() add_dependencies(Nabla ${NBL_TARGETS}) @@ -780,14 +786,6 @@ add_subdirectory(ext EXCLUDE_FROM_ALL) propagate_changed_variables_to_parent_scope() nbl_install_headers("${NABLA_HEADERS_PUBLIC}") -nbl_install_file_spec("${NBL_ROOT_PATH_BINARY}/include/nbl/builtin/builtinResources.h" "nbl/builtin") - -if(NBL_EMBED_BUILTIN_RESOURCES) - nbl_install_builtin_resources(nblBuiltinResourceData) - nbl_install_builtin_resources(spirvBuiltinResourceData) - nbl_install_builtin_resources(boostBuiltinResourceData) -endif() - set_target_properties(Nabla PROPERTIES DEBUG_POSTFIX _debug) set_target_properties(Nabla PROPERTIES RELWITHDEBINFO_POSTFIX _relwithdebinfo) @@ -875,4 +873,4 @@ source_group(TREE "${NBL_ROOT_PATH}" source_group(TREE "${NBL_ROOT_PATH}" PREFIX "Source Files" FILES ${NABLA_SOURCE_FILES} -) \ No newline at end of file +) diff --git a/src/nbl/asset/IAssetManager.cpp b/src/nbl/asset/IAssetManager.cpp index dc67ed8d01..5f48170c37 100644 --- a/src/nbl/asset/IAssetManager.cpp +++ b/src/nbl/asset/IAssetManager.cpp @@ -84,7 +84,7 @@ #endif #include "nbl/asset/interchange/CBufferLoaderBIN.h" -//#include "nbl/asset/interchange/CIESProfileLoader.h" +#include "nbl/asset/interchange/CIESProfileLoader.h" #include "nbl/asset/utils/CGeometryCreator.h" @@ -181,7 +181,7 @@ void IAssetManager::addLoadersAndWriters() #ifdef _NBL_COMPILE_WITH_GLI_WRITER_ addAssetWriter(core::make_smart_refctd_ptr(core::smart_refctd_ptr(m_system))); #endif -// addAssetLoader(core::make_smart_refctd_ptr()); +addAssetLoader(core::make_smart_refctd_ptr()); for (auto& loader : m_loaders.vector) loader->initialize(); diff --git a/src/nbl/asset/interchange/CIESProfileLoader.cpp b/src/nbl/asset/interchange/CIESProfileLoader.cpp index eecdde3190..a78c9af0a2 100644 --- a/src/nbl/asset/interchange/CIESProfileLoader.cpp +++ b/src/nbl/asset/interchange/CIESProfileLoader.cpp @@ -3,24 +3,53 @@ using namespace nbl; using namespace asset; -asset::SAssetBundle -CIESProfileLoader::loadAsset(io::IReadFile* _file, - const asset::IAssetLoader::SAssetLoadParams& _params, - asset::IAssetLoader::IAssetLoaderOverride* _override, - uint32_t _hierarchyLevel) { - if (!_file) +bool CIESProfileLoader::isALoadableFileFormat(system::IFile* _file, const system::logger_opt_ptr logger) const +{ + system::IFile::success_t success; + std::string versionBuffer(0x45, ' '); + const auto* fName = _file->getFileName().c_str(); + _file->read(success, versionBuffer.data(), 0, versionBuffer.size()); + + if (success) + { + for (const auto& it : CIESProfileParser::VALID_SIGNATURES) + if (versionBuffer.find(it.data()) != std::string::npos) + return true; + + logger.log("%s: Invalid IES signature for \"%s\" file!", system::ILogger::ELL_ERROR, __FUNCTION__, fName); + } + else + logger.log("%s: Failed to read \"%s\" file!", system::ILogger::ELL_ERROR, __FUNCTION__, fName); + + return false; +} + +asset::SAssetBundle CIESProfileLoader::loadAsset(system::IFile* _file, const asset::IAssetLoader::SAssetLoadParams& _params, asset::IAssetLoader::IAssetLoaderOverride* _override, uint32_t _hierarchyLevel) +{ + if (not _file) + { + _params.logger.log("%s: Nullptr system::IFile pointer!", system::ILogger::ELL_ERROR, __FUNCTION__); return {}; + } IAssetLoader::SAssetLoadContext loadContex(_params, _file); core::vector data(_file->getSize()); - _file->read(data.data(), _file->getSize()); + system::IFile::success_t success; + const auto* fName = _file->getFileName().c_str(); + _file->read(success, data.data(), 0, _file->getSize()); + + if (not success) + { + _params.logger.log("%s: Failed to read \"%s\" file!", system::ILogger::ELL_ERROR, __FUNCTION__, fName); + return {}; + } CIESProfileParser parser(data.data(), data.size()); CIESProfile profile; - if (!parser.parse(profile)) + if (not parser.parse(profile)) { - os::Printer::log("ERROR: Emission profile parsing error: " + std::string(parser.getErrorMsg()), ELL_ERROR); + _params.logger.log("%s: Failed to parse emission profile for \"%s\" file!", system::ILogger::ELL_ERROR, __FUNCTION__, fName); return {}; } @@ -31,7 +60,7 @@ CIESProfileLoader::loadAsset(io::IReadFile* _file, cpuImageView = _override->findDefaultAsset("nbl/builtin/image_view/dummy2d", loadContex, _hierarchyLevel).first; // note: we could also pass empty content, but this would require adjusting IAssetLoader source to not attempt to use all loaders to find the asset else { - const auto optimalResolution = profile.getOptimalIESResolution(); + const auto optimalResolution = profile.getAccessor().properties.optimalIESResolution; cpuImageView = profile.createIESTexture(0.f, false, optimalResolution.x, optimalResolution.y); } diff --git a/src/nbl/asset/interchange/CIESProfileLoader.h b/src/nbl/asset/interchange/CIESProfileLoader.h index 64ef9688ee..5eb26c861b 100644 --- a/src/nbl/asset/interchange/CIESProfileLoader.h +++ b/src/nbl/asset/interchange/CIESProfileLoader.h @@ -5,8 +5,6 @@ #define __NBL_ASSET_C_IES_PROFILE_LOADER_H_INCLUDED__ #include "nbl/asset/ICPUImage.h" -#include "nbl/asset/ICPUShader.h" - #include "nbl/asset/IAssetManager.h" #include "nbl/asset/interchange/IAssetLoader.h" @@ -25,20 +23,7 @@ class CIESProfileLoader final : public asset::IAssetLoader \return True if file seems to be loadable. */ - bool isALoadableFileFormat(io::IReadFile *_file) const override - { - const size_t begginingOfFile = _file->getPos(); - _file->seek(0ull); - std::string versionBuffer(0x45, ' '); - _file->read(versionBuffer.data(), versionBuffer.size()); - _file->seek(begginingOfFile); - - for (const auto& it : CIESProfileParser::VALID_SIGNATURES) - if (versionBuffer.find(it.data()) != std::string::npos) - return true; - - return false; - } + bool isALoadableFileFormat(system::IFile* _file, const system::logger_opt_ptr logger) const override; //! Returns an array of string literals terminated by nullptr const char **getAssociatedFileExtensions() const override @@ -55,11 +40,7 @@ class CIESProfileLoader final : public asset::IAssetLoader uint64_t getSupportedAssetTypesBitfield() const override { return asset::IAsset::ET_IMAGE_VIEW; } //! Loads an asset from an opened file, returns nullptr in case of failure. - asset::SAssetBundle - loadAsset(io::IReadFile* _file, - const asset::IAssetLoader::SAssetLoadParams& _params, - asset::IAssetLoader::IAssetLoaderOverride* _override = nullptr, - uint32_t _hierarchyLevel = 0u) override; + asset::SAssetBundle loadAsset(system::IFile* _file, const asset::IAssetLoader::SAssetLoadParams& _params, asset::IAssetLoader::IAssetLoaderOverride* _override = nullptr, uint32_t _hierarchyLevel = 0u) override; }; } // namespace nbl::asset #endif // __NBL_ASSET_C_IES_PROFILE_LOADER_H_INCLUDED__ diff --git a/src/nbl/asset/utils/CGeometryCreator.cpp b/src/nbl/asset/utils/CGeometryCreator.cpp index 2aa2e08fe5..6df230fb70 100644 --- a/src/nbl/asset/utils/CGeometryCreator.cpp +++ b/src/nbl/asset/utils/CGeometryCreator.cpp @@ -1888,5 +1888,132 @@ core::smart_refctd_ptr CGeometryCreator::createIcoSphere(fl return retval; } +core::smart_refctd_ptr CGeometryCreator::createGrid(const hlsl::uint16_t2 resolution) const +{ + using namespace hlsl; + + if (resolution.x < 2 || resolution.y < 2) + return nullptr; + + auto retval = core::make_smart_refctd_ptr(); + retval->setIndexing(IPolygonGeometryBase::TriangleStrip()); + + //! Create indices + /* + i \in [0, resolution.x - 1], j \in [0, resolution.y - 1] + logical vertex id : V(i, j) + + Eg. resolution = {5u, 4u}: + + j=3 15--16--17--18--19 + | \ | \ | \ | \ | + j=2 10--11--12--13--14 + | \ | \ | \ | \ | + j=1 5-- 6-- 7-- 8-- 9 + | \ | \ | \ | \ | + j=0 0-- 1-- 2-- 3-- 4 + i=0 1 2 3 4 + + Strip order (one draw), rows linked by 2 degenerate indices: + row 0 -> 1 (L->R): 0,5, 1,6, 2,7, 3,8, 4,9, 9,9 + row 1 -> 2 (R->L): 9,14, 8,13, 7,12, 6,11, 5,10, 5,5 + row 2 -> 3 (L->R): 5,10, 6,11, 7,12, 8,13, 9,14, 14,14 + */ + + const size_t indexCount = 2ull * resolution.x * (resolution.y - 1) + 2ull * (resolution.y - 2); + const size_t maxIndex = resolution.x * resolution.y - 1u; + + auto createIndices = [&]() -> void + { + auto indexView = createIndexView(indexCount, maxIndex); + + auto V = [&](IndexT i, IndexT j) { return IndexT(j * resolution.x + i); }; + auto* index = static_cast(indexView.src.buffer->getPointer()); + #define PUSH_INDEX(value) *index = value; ++index; + + for (IndexT j = 0u; j < resolution.y - 1; ++j) + { + if ((j & 1u) == 0) + { + for (IndexT i = 0u; i < resolution.x; ++i) + { + PUSH_INDEX(V(i, j)) + PUSH_INDEX(V(i, j + 1)) + } + + if (j + 1 < resolution.y - 1) + { + IndexT last = V(resolution.x - 1, j + 1); + PUSH_INDEX(last) + PUSH_INDEX(last) + } + } + else + { + for (int i = int(resolution.x) - 1; i >= 0; --i) + { + PUSH_INDEX(V(uint32_t(i), j)) + PUSH_INDEX(V(uint32_t(i), j + 1)) + } + + if (j + 1 < resolution.y - 1) + { + IndexT first = V(0, j + 1); + PUSH_INDEX(first) + PUSH_INDEX(first) + } + } + } + retval->setIndexView(std::move(indexView)); + }; + + if (maxIndex <= std::numeric_limits::max()) + createIndices.template operator() < uint16_t > (); + else if (maxIndex <= std::numeric_limits::max()) + createIndices.template operator() < uint32_t > (); + else + return nullptr; + + //! Create positions + const size_t vertexCount = resolution.x * resolution.y; + { + shapes::AABB<4, float32_t> aabb; + aabb.maxVx = float32_t4((resolution.x - 0.5f) / float(resolution.x), 0.5f, (resolution.y - 0.5f) / float(resolution.y), 1.f); + aabb.minVx = float32_t4(0.5f / float(resolution.x), 0.5f, 0.5f / float(resolution.y), 1.f); + + static constexpr auto stride = getTexelOrBlockBytesize(); + const auto bytes = stride * vertexCount; + auto buffer = ICPUBuffer::create({ bytes, IBuffer::EUF_NONE }); + ICPUPolygonGeometry::SDataView positionView = { + .composed = { + .encodedDataRange = {.f32 = aabb}, + .stride = stride, + .format = EF_A2R10G10B10_UNORM_PACK32, + .rangeFormat = IGeometryBase::EAABBFormat::F32 + }, + .src = {.offset = 0,.size = buffer->getSize(),.buffer = core::smart_refctd_ptr(buffer)} + }; + + auto* packed = reinterpret_cast(buffer->getPointer()); + for (uint32_t j = 0; j < resolution.y; ++j) + for (uint32_t i = 0; i < resolution.x; ++i) + { + const double u = (i + 0.5) / double(resolution.x); + const double v = (j + 0.5) / double(resolution.y); + + float64_t4 rgbaunorm = { u, 0.5, v, 1.0 }; + + *packed = {}; + encodePixels(packed, (double*)&rgbaunorm); + ++packed; + } + + retval->setPositionView(std::move(positionView)); + } + + CPolygonGeometryManipulator::recomputeContentHashes(retval.get()); + return retval; +} + } // end namespace nbl::asset diff --git a/src/nbl/asset/utils/CHLSLCompiler.cpp b/src/nbl/asset/utils/CHLSLCompiler.cpp index d36ecfa1cb..98bbeb3416 100644 --- a/src/nbl/asset/utils/CHLSLCompiler.cpp +++ b/src/nbl/asset/utils/CHLSLCompiler.cpp @@ -301,7 +301,7 @@ static DxcCompilationResult dxcCompile(const CHLSLCompiler* compiler, nbl::asset DxcBuffer sourceBuffer; sourceBuffer.Ptr = src->GetBufferPointer(); sourceBuffer.Size = src->GetBufferSize(); - sourceBuffer.Encoding = 0; + sourceBuffer.Encoding = CP_UTF8; ComPtr compileResult; res = dxc->m_dxcCompiler->Compile(&sourceBuffer, args, argCount, nullptr, IID_PPV_ARGS(compileResult.GetAddressOf())); diff --git a/src/nbl/asset/utils/CIESProfile.cpp b/src/nbl/asset/utils/CIESProfile.cpp index b507ab0d45..d4289191f6 100644 --- a/src/nbl/asset/utils/CIESProfile.cpp +++ b/src/nbl/asset/utils/CIESProfile.cpp @@ -6,115 +6,21 @@ using namespace nbl; using namespace asset; -const CIESProfile::IES_STORAGE_FORMAT CIESProfile::sample(IES_STORAGE_FORMAT theta, IES_STORAGE_FORMAT phi) const -{ - auto wrapPhi = [&](const IES_STORAGE_FORMAT& _phi) -> IES_STORAGE_FORMAT - { - constexpr auto M_HALF_PI =core::HALF_PI(); - constexpr auto M_TWICE_PI = core::PI() * 2.0; - - switch (symmetry) - { - case ISOTROPIC: //! axial symmetry - return 0.0; - case QUAD_SYMETRIC: //! phi MIRROR_REPEAT wrap onto [0, 90] degrees range - { - float wrapPhi = abs(_phi); //! first MIRROR - - if (wrapPhi > M_HALF_PI) //! then REPEAT - wrapPhi = std::clamp(M_HALF_PI - (wrapPhi - M_HALF_PI), 0, M_HALF_PI); - - return wrapPhi; //! eg. maps (in degrees) 91,269,271 -> 89 and 179,181,359 -> 1 - } - case HALF_SYMETRIC: //! phi MIRROR wrap onto [0, 180] degrees range - case OTHER_HALF_SYMMETRIC: - return abs(_phi); //! eg. maps (in degress) 181 -> 179 or 359 -> 1 - case NO_LATERAL_SYMMET: //! plot onto whole (in degress) [0, 360] range - { - if (_phi < 0) - return _phi + M_TWICE_PI; - else - return _phi; - } - default: - assert(false); - return 69; - } - }; - - const float vAngle = core::degrees(theta), hAngle = core::degrees(wrapPhi(phi)); - - assert(vAngle >= 0.0 && vAngle <= 180.0); - assert(hAngle >= 0.0 && hAngle <= 360.0); - - if (vAngle > vAngles.back()) - return 0.0; - - // bilinear interpolation - auto lb = [](const core::vector& angles, double angle) -> size_t - { - assert(!angles.empty()); - const size_t idx = std::upper_bound(std::begin(angles), std::end(angles), angle) - std::begin(angles); - return (size_t)std::max((int64_t)idx - 1, (int64_t)0); - }; - - auto ub = [](const core::vector& angles, double angle) -> size_t - { - assert(!angles.empty()); - const size_t idx = std::upper_bound(std::begin(angles), std::end(angles), angle) - std::begin(angles); - return std::min(idx, angles.size() - 1); - }; - - const size_t j0 = lb(vAngles, vAngle); - const size_t j1 = ub(vAngles, vAngle); - const size_t i0 = symmetry == ISOTROPIC ? 0 : lb(hAngles, hAngle); - const size_t i1 = symmetry == ISOTROPIC ? 0 : ub(hAngles, hAngle); - - double uResp = i1 == i0 ? 1.0 : 1.0 / (hAngles[i1] - hAngles[i0]); - double vResp = j1 == j0 ? 1.0 : 1.0 / (vAngles[j1] - vAngles[j0]); - - double u = (hAngle - hAngles[i0]) * uResp; - double v = (vAngle - vAngles[j0]) * vResp; - - double s0 = getCandelaValue(i0, j0) * (1.0 - v) + getCandelaValue(i0, j1) * (v); - double s1 = getCandelaValue(i1, j0) * (1.0 - v) + getCandelaValue(i1, j1) * (v); - - return s0 * (1.0 - u) + s1 * u; -} - -inline core::vectorSIMDf CIESProfile::octahdronUVToDir(const float& u, const float& v) -{ - core::vectorSIMDf pos = core::vectorSIMDf(2 * (u - 0.5), 2 * (v - 0.5), 0.0); - float abs_x = core::abs(pos.x), abs_y = core::abs(pos.y); - pos.z = 1.0 - abs_x - abs_y; - if (pos.z < 0.0) { - pos.x = core::sign(pos.x) * (1.0 - abs_y); - pos.y = core::sign(pos.y) * (1.0 - abs_x); - } - - return core::normalize(pos); -} - - -inline std::pair CIESProfile::sphericalDirToRadians(const core::vectorSIMDf& dir) -{ - const float theta = std::acos(std::clamp(dir.z, -1.f, 1.f)); - const float phi = std::atan2(dir.y, dir.x); - - return { theta, phi }; -} - template core::smart_refctd_ptr CIESProfile::createIESTexture(ExecutionPolicy&& policy, const float flatten, const bool fullDomainFlatten, uint32_t width, uint32_t height) const { const bool inFlattenDomain = flatten >= 0.0 && flatten <= 1.0; // [0, 1] range for blend equation, 1 is normally invalid but we use it to for special implied domain flatten mode assert(inFlattenDomain); - if (width > CDC_MAX_TEXTURE_WIDTH) - width = CDC_MAX_TEXTURE_WIDTH; + if (width > properties_t::CDC_MAX_TEXTURE_WIDTH) + width = properties_t::CDC_MAX_TEXTURE_WIDTH; + + if (height > properties_t::CDC_MAX_TEXTURE_HEIGHT) + height = properties_t::CDC_MAX_TEXTURE_HEIGHT; - if (height > CDC_MAX_TEXTURE_HEIGHT) - height = CDC_MAX_TEXTURE_HEIGHT; + // TODO: If no symmetry (no folding in half and abuse of mirror sampler) make dimensions odd-sized so middle texel taps the south pole + width = core::max(width,properties_t::CDC_MIN_TEXTURE_WIDTH); + height = core::max(height,properties_t::CDC_MIN_TEXTURE_HEIGHT); asset::ICPUImage::SCreationParams imgInfo; imgInfo.type = asset::ICPUImage::ET_2D; @@ -125,21 +31,24 @@ core::smart_refctd_ptr CIESProfile::createIESTexture(Execu imgInfo.arrayLayers = 1u; imgInfo.samples = asset::ICPUImage::ESCF_1_BIT; imgInfo.flags = static_cast(0u); - imgInfo.format = IES_TEXTURE_STORAGE_FORMAT; + imgInfo.format = properties_t::IES_TEXTURE_STORAGE_FORMAT; auto outImg = asset::ICPUImage::create(std::move(imgInfo)); asset::ICPUImage::SBufferCopy region; - constexpr auto texelBytesz = asset::getTexelOrBlockBytesize(); + constexpr auto texelBytesz = asset::getTexelOrBlockBytesize(); const size_t bufferRowLength = asset::IImageAssetHandlerBase::calcPitchInBlocks(width, texelBytesz); region.bufferRowLength = bufferRowLength; region.imageExtent = imgInfo.extent; region.imageSubresource.baseArrayLayer = 0u; region.imageSubresource.layerCount = 1u; region.imageSubresource.mipLevel = 0u; + region.imageSubresource.aspectMask = core::bitflag(asset::IImage::EAF_COLOR_BIT); region.bufferImageHeight = 0u; region.bufferOffset = 0u; - auto buffer = core::make_smart_refctd_ptr(texelBytesz * bufferRowLength * height); + asset::ICPUBuffer::SCreationParams bParams; + bParams.size = texelBytesz * bufferRowLength * height; + auto buffer = asset::ICPUBuffer::create(std::move(bParams)); if (!outImg->setBufferAndRegions(std::move(buffer), core::make_refctd_dynamic_array>(1ull, region))) return {}; @@ -150,46 +59,28 @@ core::smart_refctd_ptr CIESProfile::createIESTexture(Execu CFillImageFilter::state_type state; state.outImage = outImg.get(); - state.subresource.aspectMask = static_cast(0); + state.subresource.aspectMask = core::bitflag(asset::IImage::EAF_COLOR_BIT); state.subresource.baseArrayLayer = 0u; state.subresource.layerCount = 1u; state.outRange.extent = creationParams.extent; const IImageFilter::IState::ColorValue::WriteMemoryInfo wInfo(creationParams.format, outImg->getBuffer()->getPointer()); + const auto tInfo = texture_t::createInfo(accessor, hlsl::uint32_t2(width, height), flatten, fullDomainFlatten); - // Late Optimization TODO: Modify the Max Value for the UNORM texture to be the Max Value after flatten blending - const double maxValue = getMaxCandelaValue(); - const double maxValueRecip = 1.0 / maxValue; - - const double vertInv = 1.0 / height; - const double horiInv = 1.0 / width; - - const double flattenTarget = getAvgEmmision(fullDomainFlatten); - const double domainLo = core::radians(vAngles.front()); - const double domainHi = core::radians(vAngles.back()); auto fill = [&](uint32_t blockArrayOffset, core::vectorSIMDu32 position) -> void { - const auto dir = octahdronUVToDir(((float)position.x + 0.5) * vertInv, ((float)position.y + 0.5) * horiInv); - const auto [theta, phi] = sphericalDirToRadians(dir); - const auto intensity = sample(theta, phi); - - //! blend the IES texture with "flatten" - double blendV = intensity * (1.0 - flatten); - if (fullDomainFlatten && domainLo<=theta && theta<=domainHi || intensity >0.0) - blendV += flattenTarget * flatten; - - blendV *= maxValueRecip; + auto texel = texture_t::eval(accessor, tInfo, hlsl::uint32_t2(position.x, position.y)); asset::IImageFilter::IState::ColorValue color; - //asset::encodePixels(color.asDouble, &blendV); TODO: FIX THIS ENCODE, GIVES ARTIFACTS - const uint16_t encodeV = static_cast(std::clamp(blendV * UI16_MAX_D + 0.5, 0.0, UI16_MAX_D)); + constexpr float UI16_MAX_D = static_cast(std::numeric_limits::max()); + const uint16_t encodeV = static_cast(std::clamp(texel * UI16_MAX_D + 0.5f, 0.f, UI16_MAX_D)); // TODO: use asset::encodePixels when its fixed (no artifacts) *color.asUShort = encodeV; color.writeMemory(wInfo, blockArrayOffset); }; CBasicImageFilterCommon::clip_region_functor_t clip(state.subresource, state.outRange, creationParams.format); const auto& regions = outImg->getRegions(state.subresource.mipLevel); - CBasicImageFilterCommon::executePerRegion(std::forward(policy), outImg.get(), fill, regions.begin(), regions.end(), clip); + CBasicImageFilterCommon::executePerRegion(std::forward(policy), outImg.get(), fill, regions, clip); } ICPUImageView::SCreationParams viewParams = {}; @@ -197,7 +88,7 @@ core::smart_refctd_ptr CIESProfile::createIESTexture(Execu viewParams.flags = static_cast(0); viewParams.viewType = IImageView::ET_2D; viewParams.format = viewParams.image->getCreationParameters().format; - viewParams.subresourceRange.aspectMask = static_cast(0); + viewParams.subresourceRange.aspectMask = core::bitflag(asset::IImage::EAF_COLOR_BIT); viewParams.subresourceRange.levelCount = viewParams.image->getCreationParameters().mipLevels; viewParams.subresourceRange.layerCount = 1u; diff --git a/src/nbl/asset/utils/CIESProfile.h b/src/nbl/asset/utils/CIESProfile.h index ea3d539613..2a063e7b15 100644 --- a/src/nbl/asset/utils/CIESProfile.h +++ b/src/nbl/asset/utils/CIESProfile.h @@ -6,135 +6,70 @@ #define __NBL_ASSET_C_IES_PROFILE_H_INCLUDED__ #include "nbl/asset/metadata/CIESProfileMetadata.h" -#include "nbl/core/Types.h" -#include +#include "nbl/builtin/hlsl/ies/texture.hlsl" namespace nbl { - namespace asset - { - class CIESProfile - { - public: - using IES_STORAGE_FORMAT = double; - - //! max 16K resolution - _NBL_STATIC_INLINE_CONSTEXPR size_t CDC_MAX_TEXTURE_WIDTH = 15360; - _NBL_STATIC_INLINE_CONSTEXPR size_t CDC_MAX_TEXTURE_HEIGHT = 8640; - - _NBL_STATIC_INLINE_CONSTEXPR size_t CDC_DEFAULT_TEXTURE_WIDTH = 1024; - _NBL_STATIC_INLINE_CONSTEXPR size_t CDC_DEFAULT_TEXTURE_HEIGHT = 1024; - - _NBL_STATIC_INLINE_CONSTEXPR IES_STORAGE_FORMAT MAX_VANGLE = 180.0; - _NBL_STATIC_INLINE_CONSTEXPR IES_STORAGE_FORMAT MAX_HANGLE = 360.0; - - _NBL_STATIC_INLINE_CONSTEXPR auto UI16_MAX_D = 65535.0; - _NBL_STATIC_INLINE_CONSTEXPR auto IES_TEXTURE_STORAGE_FORMAT = asset::EF_R16_UNORM; - - enum Version : uint8_t - { - V_1995, - V_2002, - V_SIZE - }; - - enum PhotometricType : uint8_t - { - TYPE_NONE, - TYPE_C, - TYPE_B, - TYPE_A - }; - - enum LuminairePlanesSymmetry : uint8_t - { - ISOTROPIC, //! Only one horizontal angle present and a luminaire is assumed to be laterally axial symmetric - QUAD_SYMETRIC, //! The luminaire is assumed to be symmetric in each quadrant - HALF_SYMETRIC, //! The luminaire is assumed to be symmetric about the 0 to 180 degree plane - OTHER_HALF_SYMMETRIC, //! HALF_SYMETRIC case for legacy V_1995 version where horizontal angles are in range [90, 270], in that case the parser patches horizontal angles to be HALF_SYMETRIC - NO_LATERAL_SYMMET //! The luminaire is assumed to exhibit no lateral symmet - }; - - CIESProfile() = default; - ~CIESProfile() = default; - - auto getType() const { return type; } - auto getVersion() const { return version; } - auto getSymmetry() const { return symmetry; } - - const core::vector& getHoriAngles() const { return hAngles; } - const core::vector& getVertAngles() const { return vAngles; } - const core::vector& getData() const { return data; } - IES_STORAGE_FORMAT getCandelaValue(size_t i, size_t j) const { return data[vAngles.size() * i + j]; } - - IES_STORAGE_FORMAT getMaxCandelaValue() const { return maxCandelaValue; } - IES_STORAGE_FORMAT getTotalEmission() const { return totalEmissionIntegral; } - inline IES_STORAGE_FORMAT getAvgEmmision(const bool fullDomain=false) const - { - if (fullDomain) - return totalEmissionIntegral*0.25/core::radians(vAngles.back()-vAngles.front()); - return avgEmmision; - } - - auto getOptimalIESResolution() const { return optimalIESResolution; } - - template - core::smart_refctd_ptr createIESTexture(ExecutionPolicy&& policy, const float flatten = 0.0, const bool fullDomainFlatten=false, uint32_t width = CDC_DEFAULT_TEXTURE_WIDTH, uint32_t height = CDC_DEFAULT_TEXTURE_HEIGHT) const; - core::smart_refctd_ptr createIESTexture(const float flatten = 0.0, const bool fullDomainFlatten=false, uint32_t width = CDC_DEFAULT_TEXTURE_WIDTH, uint32_t height = CDC_DEFAULT_TEXTURE_HEIGHT) const; - - private: - CIESProfile(PhotometricType type, size_t hSize, size_t vSize) - : type(type), version(V_SIZE), hAngles(hSize), vAngles(vSize), data(hSize* vSize) {} - - // TODO for @Hazard, I would move it into separate file, we may use this abstraction somewhere too - //! Returns spherical coordinates with physics convention in radians - /* - https://en.wikipedia.org/wiki/Spherical_coordinate_system#/media/File:3D_Spherical.svg - Retval.first is "theta" polar angle in range [0, PI] & Retval.second "phi" is azimuthal angle - in range [-PI, PI] range +namespace asset +{ +class CIESProfile +{ + public: + CIESProfile() = default; + ~CIESProfile() = default; - Cartesian coordinates obtained from the spherical coordinates in Nabla - are assumed to have radius equal to 1 and therefore always are + struct properties_t : public nbl::hlsl::ies::ProfileProperties + { + using base_t = nbl::hlsl::ies::ProfileProperties; + NBL_CONSTEXPR_STATIC_INLINE auto IES_TEXTURE_STORAGE_FORMAT = asset::EF_R16_UNORM; + hlsl::uint32_t2 optimalIESResolution; //! Optimal resolution for IES Octahedral Candela Map texture + }; - x = cos(phi)*sin(theta) - y = sin(phi)*sin(theta) - z = cos(theta) - */ + struct accessor_t + { + using key_t = uint32_t; + using key_t2 = hlsl::uint32_t2; + using value_t = hlsl::float32_t; - static inline std::pair sphericalDirToRadians(const core::vectorSIMDf& dir); - - //! Octahedral coordinate mapping is following - /* - center is Z- - U+ from center is X+ - V+ from center is Y+ + accessor_t() = default; + accessor_t(const key_t2& resolution, const properties_t& props) : hAngles(resolution.x), vAngles(resolution.y), data(resolution.x * resolution.y), properties(props) {} + ~accessor_t() = default; - when viewed as a texture, the net folds, and the apex where the seams join is Z+ - */ - - static inline core::vectorSIMDf octahdronUVToDir(const float& u, const float& v); - - void setCandelaValue(size_t i, size_t j, IES_STORAGE_FORMAT val) { data[vAngles.size() * i + j] = val; } + template) + inline value_t vAngle(T j) const { return (value_t)vAngles[j]; } - const IES_STORAGE_FORMAT sample(IES_STORAGE_FORMAT vAngle, IES_STORAGE_FORMAT hAngle) const; + template) + inline value_t hAngle(T i) const { return (value_t)hAngles[i]; } - PhotometricType type; - Version version; - LuminairePlanesSymmetry symmetry; + template) + inline value_t value(T ij) const { return (value_t)data[vAnglesCount() * ij.x + ij.y]; } - core::vector hAngles; //! The angular displacement indegreesfrom straight down, a value represents spherical coordinate "theta" with physics convention. Note that if symmetry is OTHER_HALF_SYMMETRIC then real horizontal angle provided by IES data is (hAngles[index] + 90) - the reason behind it is we patch 1995 IES OTHER_HALF_SYMETRIC case to be HALF_SYMETRIC - core::vector vAngles; //! Measurements in degrees of angular displacement measured counterclockwise in a horizontal plane for Type C photometry and clockwise for Type A and B photometry, a value represents spherical coordinate "phi" with physics convention - core::vector data; //! Candela values - - IES_STORAGE_FORMAT maxCandelaValue = {}; //! Max value from this->data vector - IES_STORAGE_FORMAT totalEmissionIntegral = {}; //! Total energy emitted - IES_STORAGE_FORMAT avgEmmision = {}; //! this->totalEmissionIntegral / + template) + inline void setValue(T ij, value_t val) { data[vAnglesCount() * ij.x + ij.y] = val; } - core::vector2du32_SIMD optimalIESResolution; //! optimal resolution for IES profile texture + inline key_t vAnglesCount() const { return (key_t)vAngles.size(); } + inline key_t hAnglesCount() const { return (key_t)hAngles.size(); } + inline const properties_t::base_t& getProperties() const { return static_cast(properties); } - friend class CIESProfileParser; + core::vector hAngles; //! The angular displacement indegreesfrom straight down, a value represents spherical coordinate "theta" with physics convention. Note that if symmetry is OTHER_HALF_SYMMETRIC then real horizontal angle provided by IES data is (hAngles[index] + 90) - the reason behind it is we patch 1995 IES OTHER_HALF_SYMETRIC case to be HALF_SYMETRIC + core::vector vAngles; //! Measurements in degrees of angular displacement measured counterclockwise in a horizontal plane for Type C photometry and clockwise for Type A and B photometry, a value represents spherical coordinate "phi" with physics convention + core::vector data; //! Candela scalar values + properties_t properties; //! Profile properties }; - } + using texture_t = nbl::hlsl::ies::Texture; + + inline const accessor_t& getAccessor() const { return accessor; } + + template + core::smart_refctd_ptr createIESTexture(ExecutionPolicy&& policy, const float flatten = 0.0, const bool fullDomainFlatten=false, uint32_t width = properties_t::CDC_DEFAULT_TEXTURE_WIDTH, uint32_t height = properties_t::CDC_DEFAULT_TEXTURE_HEIGHT) const; + core::smart_refctd_ptr createIESTexture(const float flatten = 0.0, const bool fullDomainFlatten=false, uint32_t width = properties_t::CDC_DEFAULT_TEXTURE_WIDTH, uint32_t height = properties_t::CDC_DEFAULT_TEXTURE_HEIGHT) const; + + private: + CIESProfile(const properties_t& props, const hlsl::uint32_t2& resolution) : accessor(resolution, props) {} + accessor_t accessor; + friend class CIESProfileParser; +}; +} } #endif // __NBL_ASSET_C_IES_PROFILE_H_INCLUDED__ \ No newline at end of file diff --git a/src/nbl/asset/utils/CIESProfileParser.cpp b/src/nbl/asset/utils/CIESProfileParser.cpp index e0593cd7f2..6d9bf1ea32 100644 --- a/src/nbl/asset/utils/CIESProfileParser.cpp +++ b/src/nbl/asset/utils/CIESProfileParser.cpp @@ -33,16 +33,16 @@ bool CIESProfileParser::parse(CIESProfile& result) std::getline(ss, line); removeTrailingWhiteChars(line); - CIESProfile::Version iesVersion; - + + CIESProfile::properties_t::Version iesVersion; if (line.find(SIG_LM63_1995.data()) != std::string::npos) - iesVersion = CIESProfile::V_1995; + iesVersion = CIESProfile::properties_t::V_1995; else if (line.find(SIG_LM63_2002.data()) != std::string::npos) - iesVersion = CIESProfile::V_2002; + iesVersion = CIESProfile::properties_t::V_2002; else if (line.find(SIG_IESNA91.data()) != std::string::npos) - iesVersion = CIESProfile::V_1995; + iesVersion = CIESProfile::properties_t::V_1995; else if (line.find(SIG_ERCO_LG.data()) != std::string::npos) - iesVersion = CIESProfile::V_1995; + iesVersion = CIESProfile::properties_t::V_1995; else { errorMsg = "Unknown IESNA:LM-63 version, the IES input being parsed is invalid!"; @@ -82,9 +82,8 @@ bool CIESProfileParser::parse(CIESProfile& result) errorMsg = "unrecognized type"; return false; } - CIESProfile::PhotometricType type = - static_cast(type_); - if (type != CIESProfile::PhotometricType::TYPE_C) { + auto type = static_cast(type_); + if (type != CIESProfile::properties_t::TYPE_C) { errorMsg = "Only type C is supported for now"; return false; } @@ -100,38 +99,39 @@ bool CIESProfileParser::parse(CIESProfile& result) if (error) return false; - result = CIESProfile(type, hSize, vSize); - result.version = iesVersion; + { + CIESProfile::properties_t init; + init.setType(type); + init.setVersion(iesVersion); + init.maxCandelaValue = 0.f; + init.totalEmissionIntegral = 0.f; + init.avgEmmision = 0.f; + result = CIESProfile(init, hlsl::uint32_t2(hSize, vSize)); + } if (vSize < 2) return false; - { - const uint32_t maxDimMeasureSize = core::max(hSize, vSize); - result.optimalIESResolution = decltype(result.optimalIESResolution){ maxDimMeasureSize, maxDimMeasureSize }; - result.optimalIESResolution *= 2u; // safe bias for our bilinear interpolation to work nicely and increase resolution of a profile - } - - auto& vAngles = result.vAngles; + auto& vAngles = result.accessor.vAngles; for (int i = 0; i < vSize; i++) { - vAngles[i] = getDouble("vertical angle truncated"); + vAngles[i] = static_cast(getDouble("vertical angle truncated")); } if (!std::is_sorted(vAngles.begin(), vAngles.end())) { errorMsg = "Vertical angles should be sorted"; return false; } - if (vAngles[0] != 0.0 && vAngles[0] != 90.0) { + if (vAngles[0] != 0.f && vAngles[0] != 90.f) { errorMsg = "First vertical angle must be 0 or 90 in type C"; return false; } - if (vAngles[vSize - 1] != 90.0 && vAngles[vSize - 1] != 180.0) { + if (vAngles[vSize - 1] != 90.f && vAngles[vSize - 1] != 180.f) { errorMsg = "Last vertical angle must be 90 or 180 in type C"; return false; } - auto& hAngles = result.hAngles; + auto& hAngles = result.accessor.hAngles; for (int i = 0; i < hSize; i++) { - hAngles[i] = getDouble("horizontal angle truncated"); + hAngles[i] = static_cast(getDouble("horizontal angle truncated")); if (i != 0 && hAngles[i - 1] > hAngles[i]) return false; // Angles should be sorted } @@ -141,26 +141,26 @@ bool CIESProfileParser::parse(CIESProfile& result) const auto firstHAngle = hAngles.front(); const auto lastHAngle = hAngles.back(); - if (lastHAngle == 0) - result.symmetry = CIESProfile::ISOTROPIC; - else if (lastHAngle == 90) + if (lastHAngle == 0.f) + result.accessor.properties.setSymmetry(CIESProfile::properties_t::ISOTROPIC); + else if (lastHAngle == 90.f) { - result.symmetry = CIESProfile::QUAD_SYMETRIC; - fluxMultiplier = 4.0; + result.accessor.properties.setSymmetry(CIESProfile::properties_t::QUAD_SYMETRIC); + fluxMultiplier = 4.f; } - else if (lastHAngle == 180) + else if (lastHAngle == 180.f) { - result.symmetry = CIESProfile::HALF_SYMETRIC; + result.accessor.properties.setSymmetry(CIESProfile::properties_t::HALF_SYMETRIC); fluxMultiplier = 2.0; } - else if (lastHAngle == 360) - result.symmetry = CIESProfile::NO_LATERAL_SYMMET; + else if (lastHAngle == 360.f) + result.accessor.properties.setSymmetry(CIESProfile::properties_t::NO_LATERAL_SYMMET); else { - if (firstHAngle == 90 && lastHAngle == 270 && result.version == CIESProfile::V_1995) + if (firstHAngle == 90.f && lastHAngle == 270.f && iesVersion == CIESProfile::properties_t::V_1995) { - result.symmetry = CIESProfile::OTHER_HALF_SYMMETRIC; - fluxMultiplier = 2.0; + result.accessor.properties.setSymmetry(CIESProfile::properties_t::OTHER_HALF_SYMMETRIC); + fluxMultiplier = 2.f; for (auto& angle : hAngles) angle -= firstHAngle; // patch the profile to HALF_SYMETRIC by shifting [90,270] range to [0, 180] @@ -169,57 +169,81 @@ bool CIESProfileParser::parse(CIESProfile& result) return false; } } + const auto symmetry = result.accessor.properties.getSymmetry(); { const double factor = ballastFactor * candelaMultiplier; for (int i = 0; i < hSize; i++) for (int j = 0; j < vSize; j++) - result.setCandelaValue(i, j, factor * getDouble("intensity value truncated")); + result.accessor.setValue(hlsl::uint32_t2(i, j), static_cast(factor * getDouble("intensity value truncated"))); } float totalEmissionIntegral = 0.0, nonZeroEmissionDomainSize = 0.0; constexpr auto FULL_SOLID_ANGLE = 4.0f * core::PI(); - const auto H_ANGLES_I_RANGE = result.symmetry != CIESProfile::ISOTROPIC ? result.hAngles.size() - 1 : 1; - const auto V_ANGLES_I_RANGE = result.vAngles.size() - 1; + // TODO: this code could have two separate inner for loops for `result.symmetry != CIESProfile::ISOTROPIC` cases + const auto H_ANGLES_I_RANGE = symmetry != CIESProfile::properties_t::ISOTROPIC ? result.accessor.hAngles.size() - 1 : 1; + const auto V_ANGLES_I_RANGE = result.accessor.vAngles.size() - 1; - for (size_t i = 0; i < H_ANGLES_I_RANGE; i++) + float smallestRangeSolidAngle = FULL_SOLID_ANGLE; + for (size_t j = 0; j < V_ANGLES_I_RANGE; j++) { - const float dPhiRad = result.symmetry != CIESProfile::ISOTROPIC ? (hAngles[i + 1] - hAngles[i]) : core::PI() * 2.0f; - - for (size_t j = 0; j < V_ANGLES_I_RANGE; j++) + const float thetaRad = core::radians(result.accessor.vAngles[j]); + const float cosLo = std::cos(thetaRad); + const float cosHi = std::cos(core::radians(result.accessor.vAngles[j+1])); + const float dsinTheta = cosLo - cosHi; + + float stripIntegral = 0.f; + float nonZeroStripDomain = 0.f; + for (size_t i = 0; i < H_ANGLES_I_RANGE; i++) { - const auto candelaValue = result.getCandelaValue(i, j); + const float dPhiRad = symmetry != CIESProfile::properties_t::ISOTROPIC ? core::radians(hAngles[i + 1] - hAngles[i]) : (core::PI() * 2.0f); + // TODO: in reality one should transform the 4 vertices (or 3) into octahedral map, work out the dUV/dPhi and dUV/dTheta vectors as-if for Anisotropic Filtering + // then choose the minor axis length, and use that as a pixel size (since looking for smallest thing, dont have to worry about handling discont) + const float solidAngle = dsinTheta * dPhiRad; + if (solidAngle(result.vAngles[j]); - const float cosLo = std::cos(core::radians(result.vAngles[j])); - const float cosHi = std::cos(core::radians(result.vAngles[j + 1])); + // interpolate candela value spanned onto a solid angle + const auto candelaAverage = symmetry != CIESProfile::properties_t::ISOTROPIC ? + 0.25f * (candelaValue + result.accessor.value(hlsl::uint32_t2(i + 1, j)) + result.accessor.value(hlsl::uint32_t2(i, j + 1)) + result.accessor.value(hlsl::uint32_t2(i + 1, j + 1))) + : 0.5f * (candelaValue + result.accessor.value(hlsl::uint32_t2(i, j + 1))); - const auto differentialSolidAngle = dPhiRad*(cosLo - cosHi); - const auto integralV = candelaAverage * differentialSolidAngle; + if (result.accessor.properties.maxCandelaValue < candelaValue) + result.accessor.properties.maxCandelaValue = candelaValue; - if (integralV > 0.0) - { - totalEmissionIntegral += integralV; - nonZeroEmissionDomainSize += differentialSolidAngle; - } + stripIntegral += candelaAverage*dPhiRad; + if (candelaAverage>0.f) + nonZeroStripDomain += dPhiRad; } + totalEmissionIntegral += stripIntegral*dsinTheta; + nonZeroEmissionDomainSize += nonZeroStripDomain*dsinTheta; + } + + // assuming octahedral map + { + const uint32_t maxDimMeasureSize = core::sqrt(FULL_SOLID_ANGLE/smallestRangeSolidAngle); + result.accessor.properties.optimalIESResolution = decltype(result.accessor.properties.optimalIESResolution){ maxDimMeasureSize, maxDimMeasureSize }; + auto& res = result.accessor.properties.optimalIESResolution *= 2u; // safe bias for our bilinear interpolation to work nicely and increase resolution of a profile + res.x = core::max(res.x,CIESProfile::properties_t::CDC_MIN_TEXTURE_WIDTH); + res.y = core::max(res.y,CIESProfile::properties_t::CDC_MIN_TEXTURE_HEIGHT); } - nonZeroEmissionDomainSize = std::clamp(nonZeroEmissionDomainSize, 0.0, FULL_SOLID_ANGLE); - if (nonZeroEmissionDomainSize <= 0) // protect us from division by 0 (just in case, we should never hit it) + assert(nonZeroEmissionDomainSize >= 0.f); + //assert(nonZeroEmissionDomainSize*fluxMultiplier =approx= 2.f*(cosBack-cosFront)*PI); + if (nonZeroEmissionDomainSize <= std::numeric_limits::min()) // protect us from division by small numbers (just in case, we should never hit it) return false; - result.avgEmmision = totalEmissionIntegral / static_cast(nonZeroEmissionDomainSize); - result.totalEmissionIntegral = totalEmissionIntegral * fluxMultiplier; // we use fluxMultiplier to calculate final total emission for case where we have some symmetry between planes (fluxMultiplier is 1.0f if ISOTROPIC or NO_LATERAL_SYMMET because they already have correct total emission integral calculated), also note it doesn't affect average emission at all + result.accessor.properties.avgEmmision = totalEmissionIntegral / static_cast(nonZeroEmissionDomainSize); + result.accessor.properties.totalEmissionIntegral = totalEmissionIntegral * fluxMultiplier; // we use fluxMultiplier to calculate final total emission for case where we have some symmetry between planes (fluxMultiplier is 1.0f if ISOTROPIC or NO_LATERAL_SYMMET because they already have correct total emission integral calculated), also note it doesn't affect average emission at all + { + const float cosLo = std::cos(core::radians(result.accessor.vAngles.front())); + const float cosHi = std::cos(core::radians(result.accessor.vAngles.back())); + const float dsinTheta = cosLo - cosHi; + result.accessor.properties.fullDomainAvgEmission = result.accessor.properties.totalEmissionIntegral*(0.5f/core::PI())/dsinTheta; + } return !error; } \ No newline at end of file diff --git a/src/nbl/asset/utils/CIESProfileParser.h b/src/nbl/asset/utils/CIESProfileParser.h index cc613efc29..c5b57dd77b 100644 --- a/src/nbl/asset/utils/CIESProfileParser.h +++ b/src/nbl/asset/utils/CIESProfileParser.h @@ -99,7 +99,7 @@ namespace nbl if (!this->errorMsg) this->errorMsg = errorMsg; - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) return -1.0; else return 0; diff --git a/src/nbl/builtin/CMakeLists.txt b/src/nbl/builtin/CMakeLists.txt index 085ed3c923..7055090048 100644 --- a/src/nbl/builtin/CMakeLists.txt +++ b/src/nbl/builtin/CMakeLists.txt @@ -229,6 +229,7 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/geometry.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/intutil.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/polar.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/angle_adding.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/octahedral.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/quaternions.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/equations/quadratic.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/equations/cubic.hlsl") diff --git a/src/nbl/builtin/utils.cmake b/src/nbl/builtin/utils.cmake index d791cd3aa4..6465c2ac6d 100644 --- a/src/nbl/builtin/utils.cmake +++ b/src/nbl/builtin/utils.cmake @@ -82,10 +82,18 @@ function(ADD_CUSTOM_BUILTIN_RESOURCES _TARGET_NAME_ _BUNDLE_NAME_ _BUNDLE_SEARCH string(MAKE_C_IDENTIFIER ${_GUARD_SUFFIX_} _GUARD_SUFFIX_) set(_OUTPUT_INCLUDE_SEARCH_DIRECTORY_ "${_OUTPUT_INCLUDE_SEARCH_DIRECTORY_}") - set(_OUTPUT_HEADER_DIRECTORY_ "${_OUTPUT_INCLUDE_SEARCH_DIRECTORY_}/${_NAMESPACE_PREFIX_}") + set(_OUTPUT_HEADER_DIRECTORY_BASE "${_OUTPUT_INCLUDE_SEARCH_DIRECTORY_}/${_NAMESPACE_PREFIX_}") + set(_OUTPUT_SOURCE_DIRECTORY_BASE "${_OUTPUT_SOURCE_DIRECTORY_}") + set(_OUTPUT_INCLUDE_SEARCH_DIRECTORY_CONFIG "${_OUTPUT_INCLUDE_SEARCH_DIRECTORY_}/$") + set(_OUTPUT_HEADER_DIRECTORY_ "${_OUTPUT_INCLUDE_SEARCH_DIRECTORY_CONFIG}/${_NAMESPACE_PREFIX_}") + set(_OUTPUT_SOURCE_DIRECTORY_ "${_OUTPUT_SOURCE_DIRECTORY_BASE}/$") + set(_OUTPUT_INCLUDE_DIRECTORIES_ + "${_OUTPUT_HEADER_DIRECTORY_}" + "${_OUTPUT_INCLUDE_SEARCH_DIRECTORY_CONFIG}" + ) - file(MAKE_DIRECTORY "${_OUTPUT_HEADER_DIRECTORY_}") - file(MAKE_DIRECTORY "${_OUTPUT_SOURCE_DIRECTORY_}") + file(MAKE_DIRECTORY "${_OUTPUT_HEADER_DIRECTORY_BASE}") + file(MAKE_DIRECTORY "${_OUTPUT_SOURCE_DIRECTORY_BASE}") set(_ITR_ 0) foreach(X IN LISTS _LBR_${_BUNDLE_NAME_}_) # iterate over builtin resources bundle list given bundle name @@ -119,10 +127,10 @@ function(ADD_CUSTOM_BUILTIN_RESOURCES _TARGET_NAME_ _BUNDLE_NAME_ _BUNDLE_SEARCH list(APPEND NBL_DEPENDENCY_FILES "${NBL_BUILTIN_HEADER_GEN_PY}") list(APPEND NBL_DEPENDENCY_FILES "${NBL_BUILTIN_DATA_GEN_PY}") - set(NBL_RESOURCES_LIST_FILE "${_OUTPUT_SOURCE_DIRECTORY_}/resources.txt") + set(NBL_RESOURCES_LIST_FILE "${_OUTPUT_SOURCE_DIRECTORY_BASE}/resources-$.txt") string(REPLACE ";" "\n" RESOURCES_ARGS "${_LBR_${_BUNDLE_NAME_}_}") - file(WRITE "${NBL_RESOURCES_LIST_FILE}" "${RESOURCES_ARGS}") + file(GENERATE OUTPUT "${NBL_RESOURCES_LIST_FILE}" CONTENT "${RESOURCES_ARGS}") set(NBL_BUILTIN_RESOURCES_H "${_OUTPUT_HEADER_DIRECTORY_}/${NBL_BS_HEADER_FILENAME}") set(NBL_BUILTIN_RESOURCE_DATA_CPP "${_OUTPUT_SOURCE_DIRECTORY_}/${NBL_BS_DATA_SOURCE_FILENAME}") @@ -139,6 +147,7 @@ function(ADD_CUSTOM_BUILTIN_RESOURCES _TARGET_NAME_ _BUNDLE_NAME_ _BUNDLE_SEARCH ) add_custom_command(OUTPUT "${NBL_BUILTIN_RESOURCES_H}" "${NBL_BUILTIN_RESOURCE_DATA_CPP}" "${NBL_BUILTIN_DATA_ARCHIVE_H}" "${NBL_BUILTIN_DATA_ARCHIVE_CPP}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${_OUTPUT_HEADER_DIRECTORY_}" "${_OUTPUT_SOURCE_DIRECTORY_}" COMMAND "${_Python3_EXECUTABLE}" "${NBL_BUILTIN_HEADER_GEN_PY}" ${NBL_BUILTIN_RESOURCES_COMMON_ARGS} --outputBuiltinPath "${NBL_BUILTIN_RESOURCES_H}" --outputArchivePath "${NBL_BUILTIN_DATA_ARCHIVE_H}" --archiveBundlePath "${_BUNDLE_ARCHIVE_ABSOLUTE_PATH_}" --guardSuffix "${_GUARD_SUFFIX_}" --isSharedLibrary "${_SHARED_}" COMMAND "${_Python3_EXECUTABLE}" "${NBL_BUILTIN_DATA_GEN_PY}" ${NBL_BUILTIN_RESOURCES_COMMON_ARGS} --outputBuiltinPath "${NBL_BUILTIN_RESOURCE_DATA_CPP}" --outputArchivePath "${NBL_BUILTIN_DATA_ARCHIVE_CPP}" --bundleAbsoluteEntryPath "${_BUNDLE_SEARCH_DIRECTORY_}/${_BUNDLE_ARCHIVE_ABSOLUTE_PATH_}" --correspondingHeaderFile "${NBL_BS_HEADER_FILENAME}" --xxHash256Exe "$<${_NBL_BR_RUNTIME_HASH_}:$>" COMMENT "Generating \"${_TARGET_NAME_}\"'s sources & headers" @@ -204,8 +213,8 @@ function(ADD_CUSTOM_BUILTIN_RESOURCES _TARGET_NAME_ _BUNDLE_NAME_ _BUNDLE_SEARCH endif() target_include_directories(${_TARGET_NAME_} PUBLIC + ${_OUTPUT_INCLUDE_DIRECTORIES_} "${_NABLA_INCLUDE_DIRECTORIES_}" - "${_OUTPUT_HEADER_DIRECTORY_}" ) set_target_properties(${_TARGET_NAME_} PROPERTIES CXX_STANDARD 20) @@ -242,9 +251,9 @@ function(ADD_CUSTOM_BUILTIN_RESOURCES _TARGET_NAME_ _BUNDLE_NAME_ _BUNDLE_SEARCH _ADD_PROPERTY_(BUILTIN_RESOURCES_HEADER_DIRECTORY _OUTPUT_HEADER_DIRECTORY_) _ADD_PROPERTY_(BUILTIN_RESOURCES_SOURCE_DIRECTORY _OUTPUT_SOURCE_DIRECTORY_) _ADD_PROPERTY_(BUILTIN_RESOURCES_HEADERS NBL_BUILTIN_RESOURCES_HEADERS) - _ADD_PROPERTY_(BUILTIN_RESOURCES_INCLUDE_SEARCH_DIRECTORY _OUTPUT_INCLUDE_SEARCH_DIRECTORY_) + _ADD_PROPERTY_(BUILTIN_RESOURCES_INCLUDE_SEARCH_DIRECTORY _OUTPUT_INCLUDE_SEARCH_DIRECTORY_CONFIG) if(MSVC AND NBL_SANITIZE_ADDRESS) set_property(TARGET ${_TARGET_NAME_} PROPERTY COMPILE_OPTIONS /fsanitize=address) endif() -endfunction() \ No newline at end of file +endfunction() diff --git a/src/nbl/ext/ImGui/CMakeLists.txt b/src/nbl/ext/ImGui/CMakeLists.txt index 2c339b2b00..e46d93b952 100644 --- a/src/nbl/ext/ImGui/CMakeLists.txt +++ b/src/nbl/ext/ImGui/CMakeLists.txt @@ -33,13 +33,14 @@ target_compile_definitions(${LIB_NAME} PRIVATE _ARCHIVE_ABSOLUTE_ENTRY_PATH_="${ target_compile_definitions(${LIB_NAME} PRIVATE _ARCHIVE_ENTRY_KEY_="${_ARCHIVE_ENTRY_KEY_}") if(NBL_EMBED_BUILTIN_RESOURCES) - # (*) -> I wish we could just take NSC, offline-precompile to SPIRV, embed into builtin resource library (as we did!) but then be smart & adjust at runtime OpDecorate of our resources according to wishes - unfortunately no linker yet we have and we are not going to make one ourselves so we compile imgui shaders at runtime set(_BR_TARGET_ extImguibuiltinResourceData) + # TODO: one uber shader and common.hlsl LIST_BUILTIN_RESOURCE(RESOURCES_TO_EMBED "common.hlsl") + LIST_BUILTIN_RESOURCE(RESOURCES_TO_EMBED "pc.hlsl") LIST_BUILTIN_RESOURCE(RESOURCES_TO_EMBED "psinput.hlsl") - LIST_BUILTIN_RESOURCE(RESOURCES_TO_EMBED "vertex.hlsl") # (*) -> this we could precompile [no resources for which set/binding Ixs could be adjusted] but I'm not going to mix stuff - LIST_BUILTIN_RESOURCE(RESOURCES_TO_EMBED "fragment.hlsl") # (*) -> but this we could not since we let users to provide external descriptor set layout + ImGUI textures & sampler state set/binding Ixs (for pipeline layout) at runtime + LIST_BUILTIN_RESOURCE(RESOURCES_TO_EMBED "vertex.hlsl") + LIST_BUILTIN_RESOURCE(RESOURCES_TO_EMBED "fragment.hlsl") ADD_CUSTOM_BUILTIN_RESOURCES(${_BR_TARGET_} RESOURCES_TO_EMBED "${_ARCHIVE_ABSOLUTE_ENTRY_PATH_}" "${_ARCHIVE_ENTRY_KEY_}" "nbl::ext::imgui::builtin" "${_OUTPUT_DIRECTORY_HEADER_}" "${_OUTPUT_DIRECTORY_SOURCE_}") LINK_BUILTIN_RESOURCES_TO_TARGET(${LIB_NAME} ${_BR_TARGET_}) diff --git a/src/nbl/ext/ImGui/ImGui.cpp b/src/nbl/ext/ImGui/ImGui.cpp index f477e96cdf..fbb33f022f 100644 --- a/src/nbl/ext/ImGui/ImGui.cpp +++ b/src/nbl/ext/ImGui/ImGui.cpp @@ -154,6 +154,14 @@ core::smart_refctd_ptr UI::createPipeline(SCreation smart_refctd_ptr vertex, fragment; } shaders; + if (creationParams.spirv.has_value()) + { + // TODO: since prebuild is experminetal currently I don't validate anything + auto& spirv = creationParams.spirv.value(); + shaders.vertex = spirv.vertex; + shaders.fragment = spirv.fragment; + } + else { //! proxy the system, we will touch it gently auto system = smart_refctd_ptr(creationParams.assetManager->getSystem()); @@ -269,18 +277,18 @@ core::smart_refctd_ptr UI::createPipeline(SCreation shaders.vertex = createShader.template operator() < NBL_CORE_UNIQUE_STRING_LITERAL_TYPE("vertex.hlsl"), IShader::E_SHADER_STAGE::ESS_VERTEX > (); shaders.fragment = createShader.template operator() < NBL_CORE_UNIQUE_STRING_LITERAL_TYPE("fragment.hlsl"), IShader::E_SHADER_STAGE::ESS_FRAGMENT > (); + } - if (!shaders.vertex) - { - creationParams.utilities->getLogger()->log("Failed to compile vertex shader!", ILogger::ELL_ERROR); - return nullptr; - } + if (!shaders.vertex) + { + creationParams.utilities->getLogger()->log("Failed to create vertex shader!", ILogger::ELL_ERROR); + return nullptr; + } - if (!shaders.fragment) - { - creationParams.utilities->getLogger()->log("Failed to compile fragment shader!", ILogger::ELL_ERROR); - return nullptr; - } + if (!shaders.fragment) + { + creationParams.utilities->getLogger()->log("Failed to create fragment shader!", ILogger::ELL_ERROR); + return nullptr; } SVertexInputParams vertexInputParams{};