├── .clang-format ├── .editorconfig ├── .gitignore ├── CMakeLists.txt ├── CONTRIBUTING ├── ImHex └── bary.hexpat ├── LICENSE ├── README.md ├── SECURITY.md ├── fuzz ├── bary_fuzz.cpp └── bary_fuzz.dict ├── include ├── bary │ ├── bary_api.h │ ├── bary_core.h │ └── bary_types.h └── baryutils │ └── baryutils.h ├── src ├── bary_core.cpp └── bary_utils.cpp ├── test └── main.cpp └── tool ├── README.md ├── bary_tool.cpp ├── filemapping.cpp └── filemapping.hpp /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | AccessModifierOffset: '-2' 3 | AlignAfterOpenBracket: Align 4 | AlignConsecutiveAssignments: 'true' 5 | AlignConsecutiveDeclarations: 'true' 6 | AlignOperands: 'true' 7 | AlignTrailingComments: 'true' 8 | AllowAllParametersOfDeclarationOnNextLine: 'false' 9 | AllowShortBlocksOnASingleLine: 'false' 10 | AllowShortCaseLabelsOnASingleLine: 'false' 11 | AllowShortFunctionsOnASingleLine: Inline 12 | AllowShortIfStatementsOnASingleLine: 'false' 13 | AllowShortLoopsOnASingleLine: 'false' 14 | AlwaysBreakAfterReturnType: None 15 | AlwaysBreakBeforeMultilineStrings: 'true' 16 | AlwaysBreakTemplateDeclarations: 'true' 17 | BinPackArguments: 'true' 18 | BinPackParameters: 'false' 19 | ExperimentalAutoDetectBinPacking: 'false' 20 | BreakBeforeBinaryOperators: NonAssignment 21 | BreakBeforeBraces: Custom 22 | BreakBeforeTernaryOperators: 'false' 23 | BreakConstructorInitializersBeforeComma: 'true' 24 | ColumnLimit: '120' 25 | ConstructorInitializerAllOnOneLineOrOnePerLine: 'false' 26 | Cpp11BracedListStyle: 'true' 27 | IndentCaseLabels: 'false' 28 | IndentWidth: '4' 29 | KeepEmptyLinesAtTheStartOfBlocks: 'false' 30 | Language: Cpp 31 | MaxEmptyLinesToKeep: '2' 32 | NamespaceIndentation: None 33 | ObjCSpaceBeforeProtocolList: 'true' 34 | PointerAlignment: Left 35 | SpaceAfterCStyleCast: 'false' 36 | SpaceBeforeAssignmentOperators: 'true' 37 | SpaceBeforeParens: Never 38 | SpaceInEmptyParentheses: 'false' 39 | SpacesBeforeTrailingComments: '2' 40 | SpacesInAngles: 'false' 41 | SpacesInCStyleCastParentheses: 'false' 42 | SpacesInParentheses: 'false' 43 | SpacesInSquareBrackets: 'false' 44 | Standard: Cpp11 45 | TabWidth: '2' 46 | UseTab: Never 47 | SortIncludes: 'false' 48 | ReflowComments: 'false' 49 | BraceWrapping: { 50 | AfterClass: 'true' 51 | AfterControlStatement: 'true' 52 | AfterEnum: 'true' 53 | AfterFunction: 'true' 54 | AfterNamespace: 'true' 55 | AfterStruct: 'true' 56 | AfterUnion: 'true' 57 | BeforeCatch: 'true' 58 | BeforeElse: 'true' 59 | IndentBraces: 'false' 60 | } 61 | PenaltyExcessCharacter: 1 62 | PenaltyBreakBeforeFirstCallParameter: 40 63 | PenaltyBreakFirstLessLess: 1 64 | PenaltyBreakComment: 30 65 | PenaltyBreakString: 30 66 | PenaltyReturnTypeOnItsOwnLine: 9999 67 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # This is the top-most editor config file 2 | root = true 3 | 4 | # Default to 4 space indentation for C/C++ files 5 | [*.{c,cpp,h,hpp,inl}] 6 | indent_size = 4 7 | indent_style = space 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | _build 3 | inst*/ 4 | _inst*/ 5 | **/Debug 6 | **/Release/* 7 | **/CMakeFiles 8 | **/cmake_install.cmake 9 | /.vs 10 | .vscode/ 11 | *.sln 12 | *.vcxproj 13 | *.vcxproj.filters 14 | *.vcxproj.user 15 | cmake_build -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | # Set the C/C++ specified in the projects as requirements 4 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 5 | set(CMAKE_C_STANDARD_REQUIRED ON) 6 | set(CMAKE_CXX_STANDARD 17) 7 | 8 | project(bary_core VERSION 1.0.0) 9 | 10 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 11 | 12 | if(UNIX) 13 | add_definitions(-DLINUX) 14 | endif() 15 | 16 | # XXXX we might be able to force this by setting CMAKE_GENERATOR_PLATFORM ?? 17 | if(NOT (CMAKE_SIZEOF_VOID_P EQUAL 8)) 18 | message( FATAL_ERROR "64-bit builds only : reconfigure with -A x64" ) 19 | endif() 20 | 21 | set(CMAKE_DEBUG_POSTFIX "_d") 22 | set(CMAKE_RELWITHDEBINFO_POSTFIX "_rd") 23 | 24 | # 25 | # build 26 | # 27 | 28 | # If we need sanitizers enabled, add them here so that they apply to all 29 | # bary_core projects. 30 | # Note that libFuzzer is included in this list, because the fuzzer is a 31 | # sanitizer, and emits instrumentation code to e.g. track the instruction 32 | # counter. This instrumentation helps the fuzzer find new code paths. 33 | option(BARY_CORE_BUILD_FUZZER "Enables the bary_fuzz target for fuzz-testing using libFuzzer. This should be off when creating a production build, for performance, since it emits program counter instrumentation code in bary_core." OFF) 34 | set(BARY_CORE_USE_SANITIZERS "" CACHE STRING "Semicolon-separated list of LLVM sanitizers to compile with (example: address;memory). This should be empty when creating a production build, for performance.") 35 | if(BARY_CORE_BUILD_FUZZER OR BARY_CORE_USE_SANITIZERS) 36 | set(_SANITIZERS ${BARY_CORE_USE_SANITIZERS}) 37 | if(BARY_CORE_BUILD_FUZZER) 38 | list(APPEND _SANITIZERS "fuzzer") 39 | endif() 40 | if(MSVC) 41 | foreach(_SANITIZER IN ITEMS ${_SANITIZERS}) 42 | add_compile_options("/fsanitize=${_SANITIZER}") 43 | endforeach() 44 | elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") 45 | list(JOIN _SANITIZERS "," _SANITIZERS_COMMA_JOINED) 46 | add_compile_options(-fsanitize=${_SANITIZERS_COMMA_JOINED} -fno-sanitize-recover=all) 47 | add_link_options(-fsanitize=${_SANITIZERS_COMMA_JOINED} -fno-sanitize-recover=all) 48 | endif() 49 | endif() 50 | 51 | # core lib 52 | 53 | set(CORE_PUBLIC_HEADER_FILES 54 | include/bary/bary_api.h 55 | include/bary/bary_types.h 56 | include/bary/bary_core.h 57 | ) 58 | 59 | set(CORE_SOURCE_FILES 60 | src/bary_core.cpp 61 | ) 62 | 63 | source_group("public_include" FILES ${CORE_PUBLIC_HEADER_FILES}) 64 | source_group("source" FILES ${CORE_SOURCE_FILES}) 65 | 66 | include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_BINARY_DIR}") 67 | 68 | add_library(bary_core STATIC ${CORE_SOURCE_FILES} ${CORE_PUBLIC_HEADER_FILES}) 69 | 70 | set_target_properties(bary_core PROPERTIES FOLDER "bary_core") 71 | 72 | target_include_directories(bary_core PUBLIC 73 | $ $/include>) 74 | 75 | install(FILES ${CORE_PUBLIC_HEADER_FILES} DESTINATION "include") 76 | install(TARGETS bary_core DESTINATION "lib" EXPORT "baryCoreTargets") 77 | install(EXPORT "baryCoreTargets" FILE "baryCoreTargets.cmake" EXPORT_LINK_INTERFACE_LIBRARIES DESTINATION "cmake") 78 | 79 | 80 | set(BARY_CORE_BUILD_TESTS OFF CACHE BOOL "Build the unit tests for bary_core") 81 | set(BARY_CORE_BUILD_UTILS OFF CACHE BOOL "Build the utility library for bary_core") 82 | set(BARY_CORE_BUILD_TOOL OFF CACHE BOOL "Build the tool for bary") 83 | 84 | # utils lib 85 | 86 | if (BARY_CORE_BUILD_UTILS OR BARY_CORE_BUILD_TOOL) 87 | set(UTILS_PUBLIC_HEADER_FILES 88 | include/baryutils/baryutils.h 89 | ) 90 | 91 | set(UTILS_SOURCE_FILES 92 | src/bary_utils.cpp 93 | ) 94 | 95 | source_group("public_include" FILES ${UTILS_PUBLIC_HEADER_FILES}) 96 | source_group("source" FILES ${UTILS_SOURCE_FILES}) 97 | 98 | include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_BINARY_DIR}") 99 | 100 | add_library(bary_utils STATIC ${UTILS_SOURCE_FILES} ${UTILS_PUBLIC_HEADER_FILES}) 101 | 102 | set_target_properties(bary_utils PROPERTIES FOLDER "bary_utils") 103 | 104 | target_include_directories(bary_utils PUBLIC 105 | $ $/include>) 106 | 107 | install(FILES ${UTILS_PUBLIC_HEADER_FILES} DESTINATION "include") 108 | install(TARGETS bary_utils DESTINATION "lib" EXPORT "baryUtilsTargets") 109 | install(EXPORT "baryUtilsTargets" FILE "baryUtilsTargets.cmake" EXPORT_LINK_INTERFACE_LIBRARIES DESTINATION "cmake") 110 | endif() 111 | 112 | # tests 113 | 114 | if(BARY_CORE_BUILD_TESTS) 115 | set(TEST_FILES test/main.cpp) 116 | source_group("tests" FILES ${TEST_FILES}) 117 | add_executable(bary_core_test ${TEST_FILES}) 118 | target_link_libraries(bary_core_test bary_core) 119 | set_target_properties(bary_core_test PROPERTIES FOLDER "bary_core") 120 | endif() 121 | 122 | # tool 123 | if(BARY_CORE_BUILD_TOOL) 124 | set(TOOL_FILES 125 | tool/filemapping.cpp 126 | tool/filemapping.hpp 127 | tool/bary_tool.cpp) 128 | source_group("tool" FILES ${TOOL_FILES}) 129 | add_executable(bary_tool ${TOOL_FILES}) 130 | target_link_libraries(bary_tool bary_core bary_utils) 131 | set_target_properties(bary_tool PROPERTIES FOLDER "bary_core") 132 | endif() 133 | 134 | # fuzzer 135 | if(BARY_CORE_BUILD_FUZZER) 136 | set(FUZZER_FILES fuzz/bary_fuzz.cpp) 137 | source_group("fuzzer" FILES ${FUZZER_FILES}) 138 | add_executable(bary_fuzzer ${FUZZER_FILES}) 139 | target_link_libraries(bary_fuzzer bary_core) 140 | file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/CORPUS) 141 | set_target_properties(bary_fuzzer PROPERTIES 142 | FOLDER "bary_core" 143 | VS_DEBUGGER_COMMAND_ARGUMENTS "CORPUS -dict=${CMAKE_CURRENT_LIST_DIR}/fuzz/bary_fuzz.dict" 144 | ) 145 | endif() 146 | 147 | # 148 | # package target 149 | # 150 | 151 | set(CPACK_GENERATOR "ZIP") 152 | set(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}") 153 | set(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}") 154 | set(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}") 155 | include(CPack) 156 | -------------------------------------------------------------------------------- /CONTRIBUTING: -------------------------------------------------------------------------------- 1 | https://developercertificate.org/ 2 | 3 | Developer Certificate of Origin 4 | Version 1.1 5 | 6 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 7 | 8 | Everyone is permitted to copy and distribute verbatim copies of this 9 | license document, but changing it is not allowed. 10 | 11 | 12 | Developer's Certificate of Origin 1.1 13 | 14 | By making a contribution to this project, I certify that: 15 | 16 | (a) The contribution was created in whole or in part by me and I 17 | have the right to submit it under the open source license 18 | indicated in the file; or 19 | 20 | (b) The contribution is based upon previous work that, to the best 21 | of my knowledge, is covered under an appropriate open source 22 | license and I have the right under that license to submit that 23 | work with modifications, whether created in whole or in part 24 | by me, under the same open source license (unless I am 25 | permitted to submit under a different license), as indicated 26 | in the file; or 27 | 28 | (c) The contribution was provided directly to me by some other 29 | person who certified (a), (b) or (c) and I have not modified 30 | it. 31 | 32 | (d) I understand and agree that this project and the contribution 33 | are public and that a record of the contribution (including all 34 | personal information I submit with it, including my sign-off) is 35 | maintained indefinitely and may be redistributed consistent with 36 | this project or the open source license(s) involved. -------------------------------------------------------------------------------- /ImHex/bary.hexpat: -------------------------------------------------------------------------------- 1 | // 2 | // SPDX-FileCopyrightText: Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | // 17 | 18 | // This is an ImHex (imhex.werwolv.net) hex editor pattern file for 19 | // analyzing .bary files. These have the 16-byte magic number 20 | // AB 42 41 52 59 20 30 30 31 30 30 BB 0D 0A 1A 0A 21 | // (text) B A R Y 0 0 1 0 0 22 | // To use it inside ImHex, go to File > Load Pattern... and then select 23 | // this file. Alternatively, one can install this file inside ImHex's 24 | // patterns/ folder. I recommend creating an ImHex project file using 25 | // File > Save Project...; that makes it faster to reload .bary files 26 | // when they change. 27 | // 28 | // This has two main limitations at the moment: 29 | // 30 | // - Only the first array_limit values in an array are displayed. This 31 | // because ImHex halts execution prematurely if an array has too many 32 | // elements, and .bary files can be relatively large. 33 | // 34 | // - There is no security guarantee or validation. 35 | // 36 | // Please also note that the documentation here is informative, not 37 | // normative, and is copied from the bary_core headers. Please see 38 | // bary_core's headers for the latest documentation. 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | #define ARRAY_LIMIT 0x1000 46 | #pragma array_limit 0x1000 47 | 48 | enum ValueLayout : uint32_t 49 | { 50 | eUndefined, 51 | eTriangleUmajor, 52 | eTriangleBirdCurve, 53 | }; 54 | 55 | enum ValueFrequency : uint32_t 56 | { 57 | eUndefined, 58 | ePerVertex, 59 | ePerTriangle, 60 | }; 61 | 62 | enum ValueSemanticType : uint32_t 63 | { 64 | eGeneric, 65 | eDisplacement, 66 | }; 67 | 68 | enum Format : uint32_t 69 | { 70 | // enum values match VK_FORMAT 71 | 72 | eUndefined = 0, 73 | eR8_unorm = 9, 74 | eR8_snorm = 10, 75 | eR8_uint = 13, 76 | eR8_sint = 14, 77 | eRG8_unorm = 16, 78 | eRG8_snorm = 17, 79 | eRG8_uint = 20, 80 | eRG8_sint = 21, 81 | eRGB8_unorm = 23, 82 | eRGB8_snorm = 24, 83 | eRGB8_uint = 27, 84 | eRGB8_sint = 28, 85 | eRGBA8_unorm = 37, 86 | eRGBA8_snorm = 38, 87 | eRGBA8_uint = 41, 88 | eRGBA8_sint = 42, 89 | eR16_unorm = 70, 90 | eR16_snorm = 71, 91 | eR16_uint = 74, 92 | eR16_sint = 75, 93 | eR16_sfloat = 76, 94 | eRG16_unorm = 77, 95 | eRG16_snorm = 78, 96 | eRG16_uint = 81, 97 | eRG16_sint = 82, 98 | eRG16_sfloat = 83, 99 | eRGB16_unorm = 84, 100 | eRGB16_snorm = 85, 101 | eRGB16_uint = 88, 102 | eRGB16_sint = 89, 103 | eRGB16_sfloat = 90, 104 | eRGBA16_unorm = 91, 105 | eRGBA16_snorm = 92, 106 | eRGBA16_uint = 95, 107 | eRGBA16_sint = 96, 108 | eRGBA16_sfloat = 97, 109 | eR32_uint = 98, 110 | eR32_sint = 99, 111 | eR32_sfloat = 100, 112 | eRG32_uint = 101, 113 | eRG32_sint = 102, 114 | eRG32_sfloat = 103, 115 | eRGB32_uint = 104, 116 | eRGB32_sint = 105, 117 | eRGB32_sfloat = 106, 118 | eRGBA32_uint = 107, 119 | eRGBA32_sint = 108, 120 | eRGBA32_sfloat = 109, 121 | eR64_uint = 110, 122 | eR64_sint = 111, 123 | eR64_sfloat = 112, 124 | eRG64_uint = 113, 125 | eRG64_sint = 114, 126 | eRG64_sfloat = 115, 127 | eRGB64_uint = 116, 128 | eRGB64_sint = 117, 129 | eRGB64_sfloat = 118, 130 | eRGBA64_uint = 119, 131 | eRGBA64_sint = 120, 132 | eRGBA64_sfloat = 121, 133 | 134 | 135 | // opacity encoding (based on VK NV extension reservation 397) 136 | 137 | // block-compressed opacity format 138 | // for uncompressed 1 or 2 bit data stored in 8-bit 139 | // valueByteSize = 1 140 | eOpaC1_rx_uint_block = 1000396000, 141 | 142 | // displacement encoding (based on VK NV extension reservation 398) 143 | 144 | // block-compressed displacement format 145 | // for compressed data stored in blocks of 512- or 1024-bit 146 | // valueByteSize = 1 147 | // valueByteAlignment = 128 148 | // minmax as eR11_unorm_pack16 149 | eDispC1_r11_unorm_block = 1000397000, 150 | 151 | // for uncompressed data 1 x 11 bit stored in 16-bit 152 | eR11_unorm_pack16 = 1000397001, 153 | 154 | // variable packed format 155 | // for uncompressed data 1 x 11 bit densely packed sequence of 32bit values. 156 | // Each triangle starts at a 32-bit boundary. 157 | // valueByteSize = 1 158 | // minmax as eR11_unorm_pack16 159 | eR11_unorm_packed_align32 = 1000397002, 160 | }; 161 | 162 | enum BlockFormatOpaC1 : uint16_t 163 | { 164 | eInvalid = 0, 165 | eR1_uint_x8 = 1, 166 | eR2_uint_x4 = 2, 167 | }; 168 | 169 | enum BlockFormatDispC1 : uint16_t 170 | { 171 | eInvalid = 0, 172 | eR11_unorm_lvl3_pack512 = 1, 173 | eR11_unorm_lvl4_pack1024 = 2, 174 | eR11_unorm_lvl5_pack1024 = 3, 175 | }; 176 | 177 | struct ValueFloatVector 178 | { 179 | float r; 180 | float g; 181 | float b; 182 | float a; 183 | }; 184 | 185 | ////////////////////////////////////////////////////////////////////////// 186 | // Bary File Memory Layout 187 | // 188 | // - Preamble 189 | // - `Header` 190 | // - array of `PropertyInfo` (pointed to by `Header::propertyInfoRange`) 191 | // - data for all properties (pointed to by `PropertyInfo::range`) 192 | // - data might be interleaved with `PropertyInfo::supercompressionGlobalData` 193 | // after each property 194 | // 195 | // All byte alignments must be power of 2 and at least 4 bytes 196 | 197 | struct ByteRange 198 | { 199 | // Unless mentioned otherwise, must be 4-byte aligned 200 | uint64_t byteOffset; 201 | uint64_t byteLength; 202 | }; 203 | 204 | struct Header 205 | { 206 | char versionIdentifier[16]; 207 | // size includes sizeof(header) and all subsequent properties 208 | uint64_t totalByteSize; 209 | // stores PropertyInfo[] 210 | // all PropertyInfo byte ranges must come after header and stay within 211 | // totalByteSize; their ranges must be non-overlapping and 212 | // ordered with ascending ByteRange::byteOffset 213 | ByteRange propertyInfoRange; 214 | }; 215 | 216 | // Represents a bary property UUID. 217 | // Note that this is not the same as StandardPropertyType - although we use 218 | // the same names, we include the GUIDs here directly so they're readable in 219 | // the Pattern Data box. Additionally, anyone can generate new GUIDs to add 220 | // custom properties to a .bary file. 221 | // The straightforward approach of `enum UUID : u128` doesn't quite seem to 222 | // work - when reading, the top 64 bits are filled with 0s - so we rely upon 223 | // how the lower 64 bits are unique for all known bary UUIDs. 224 | using UUIDHi = u64; 225 | enum UUIDLo : u64 226 | { 227 | // stores ValueInfo + values 228 | eValues = 0xc9e044d5b44daa04, 229 | // stores Group[] 230 | eGroups = 0x9dc4451739ee40d0, 231 | // stores Triangle[] 232 | eTriangles = 0xee59426c00458e68, 233 | 234 | // stores TriangleMinMaxsInfo + data 235 | eTriangleMinMaxs = 0x56744eb723010706, 236 | 237 | // stores TriangleUncompressedMip[] 238 | // info for uncompressed lower resolution mip-level for triangles 239 | // (can be sparse) 240 | // useful to accelerate decoding of block format compression 241 | eTriangleUncompressedMips = 0xcc2b41efd8b5df1c, 242 | // stores UncompressedMipsInfo + data 243 | // uncompressed mip data referenced by TriangleUncompressedMip 244 | eUncompressedMips = 0xcb334423585a66e6, 245 | // stores GroupUncompressedMip[] 246 | // must match Group[] count 247 | eGroupUncompressedMips = 0xc47b460357094946, 248 | 249 | // stores HistogramEntry[] 250 | eHistogramEntries = 0x839a438a6e756bc1, 251 | // stores GroupHistogramRange[] per group 252 | eGroupHistograms = 0x76dd4337400d972f, 253 | 254 | // Mesh properties: 255 | // may, or may not be stored within a bary file 256 | // Depending on the application and 3d mesh file 257 | // that this bary file is used with, these properties may be stored 258 | // in the 3d mesh file itself or referenced as being part of the bary file. 259 | // It is important to note that a strong coupling between 3d mesh 260 | // and its barycentric data exist. 261 | 262 | // stores MeshGroup[] per mesh group 263 | eMeshGroups = 0x7ec347f094beebda, 264 | 265 | // stores MeshHistogramEntry[] 266 | eMeshHistogramEntries = 0x7f4448f368ce84e0, 267 | // stores MeshGroupHistogramRange[] per mesh group 268 | // meshTriangleMappings are applied to account 269 | // for bary::Triangle being instanced from 270 | // multiple mesh triangles. 271 | eMeshGroupHistograms = 0x7456405602a37898, 272 | 273 | // stores {MeshDirectionsInfo + data} 274 | // used for displacement, 3d mesh per-vertex 275 | eMeshDisplacementDirections = 0xb9284aebf262d687, 276 | 277 | // stores MeshDirectionBoundsInfo + data 278 | // used for displacement, 3d mesh per-vertex 279 | eMeshDisplacementDirectionBounds = 0x29234ae125bf3c65, 280 | 281 | // stores MeshTriangleMappingsInfo + data 282 | // index to map a 3d mesh triangle to a bary file triangle 283 | eMeshTriangleMappings = 0x92bb4a8a9cdc3ad0, 284 | 285 | // stores MeshTriangleFlagsInfo + data 286 | // special per mesh triangle flags: 287 | // - currently 1 bit per edge, if set means the neighboring triangle has 288 | // one subdivision level less. Edge order for the triangle (v0,v1,v2) 289 | // is (v0,v1) (v1,v2) (v2,v0) 290 | eMeshTriangleFlags = 0x4ec3497490f9eed3, 291 | 292 | // stores MeshPositionsInfo + data 293 | // bary files typically don't store this, but useful for debugging 294 | // or asserting the 3d mesh matches expectations / triangle winding... 295 | eMeshPositions = 0xc01d430dac071cfe, 296 | 297 | // stores MeshTrianglesInfo + data 298 | // bary files typically don't store this, but useful for debugging 299 | // or asserting the 3d mesh matches expectations / triangle winding... 300 | eMeshTriangleIndices = 0x1daf410f48f106db, 301 | }; 302 | 303 | enum SupercompressionScheme : u32 304 | { 305 | eNone = 0, 306 | }; 307 | 308 | // Clamps an array length to the ImHex maximum. 309 | fn clampArray(u128 len){ 310 | if (len > ARRAY_LIMIT) 311 | return ARRAY_LIMIT; 312 | else 313 | return len; 314 | }; 315 | 316 | ////////////////////////////////////////////////////////////////////////// 317 | // Format visualization structures 318 | 319 | // The pattern language doesn't currently have half floats, 320 | // so at the moment we display half floats as their raw data. 321 | // Similarly, we display UNORM values as integers. 322 | 323 | struct RG8_u 324 | { 325 | u8 r; 326 | u8 g; 327 | }; 328 | 329 | struct RG8_s 330 | { 331 | s8 r; 332 | s8 g; 333 | }; 334 | 335 | struct RGB8_u 336 | { 337 | u8 r; 338 | u8 g; 339 | u8 b; 340 | }; 341 | 342 | struct RGB8_s 343 | { 344 | s8 r; 345 | s8 g; 346 | s8 b; 347 | }; 348 | 349 | struct RGBA8_u 350 | { 351 | u8 r; 352 | u8 g; 353 | u8 b; 354 | u8 a; 355 | }; 356 | 357 | struct RGBA8_s 358 | { 359 | s8 r; 360 | s8 g; 361 | s8 b; 362 | s8 a; 363 | }; 364 | 365 | struct RG16_u 366 | { 367 | u16 r; 368 | u16 g; 369 | }; 370 | 371 | struct RG16_s 372 | { 373 | s16 r; 374 | s16 g; 375 | }; 376 | 377 | struct RGB16_u 378 | { 379 | u16 r; 380 | u16 g; 381 | u16 b; 382 | }; 383 | 384 | struct RGB16_s 385 | { 386 | s16 r; 387 | s16 g; 388 | s16 b; 389 | }; 390 | 391 | struct RGBA16_u 392 | { 393 | u16 r; 394 | u16 g; 395 | u16 b; 396 | u16 a; 397 | }; 398 | 399 | struct RGBA16_s 400 | { 401 | s16 r; 402 | s16 g; 403 | s16 b; 404 | s16 a; 405 | }; 406 | 407 | struct R16_sfloat 408 | { 409 | u16 r_u16; 410 | }; 411 | 412 | struct RG16_sfloat 413 | { 414 | u16 r_u16; 415 | u16 g_u16; 416 | }; 417 | 418 | struct RGB16_sfloat 419 | { 420 | u16 r_u16; 421 | u16 g_u16; 422 | u16 b_u16; 423 | }; 424 | 425 | struct RGBA16_sfloat 426 | { 427 | u16 r_u16; 428 | u16 g_u16; 429 | u16 b_u16; 430 | u16 a_u16; 431 | }; 432 | 433 | struct RG32_u 434 | { 435 | u32 r; 436 | u32 g; 437 | }; 438 | struct RG32_s 439 | { 440 | s32 r; 441 | s32 g; 442 | }; 443 | 444 | struct RGB32_u 445 | { 446 | u32 r; 447 | u32 g; 448 | u32 b; 449 | }; 450 | 451 | struct RGB32_s 452 | { 453 | s32 r; 454 | s32 g; 455 | s32 b; 456 | }; 457 | 458 | struct RGBA32_u 459 | { 460 | u32 r; 461 | u32 g; 462 | u32 b; 463 | u32 a; 464 | }; 465 | 466 | struct RGBA32_s 467 | { 468 | s32 r; 469 | s32 g; 470 | s32 b; 471 | s32 a; 472 | }; 473 | 474 | struct RG32_sfloat 475 | { 476 | float r; 477 | float g; 478 | }; 479 | 480 | struct RGB32_sfloat 481 | { 482 | float r; 483 | float g; 484 | float b; 485 | }; 486 | 487 | struct RGBA32_sfloat 488 | { 489 | float r; 490 | float g; 491 | float b; 492 | float a; 493 | }; 494 | 495 | struct RG64_u 496 | { 497 | u64 r; 498 | u64 g; 499 | }; 500 | 501 | struct RG64_s 502 | { 503 | s64 r; 504 | s64 g; 505 | }; 506 | 507 | struct RGB64_u 508 | { 509 | u32 r; 510 | u32 g; 511 | u32 b; 512 | }; 513 | 514 | struct RGB64_s 515 | { 516 | s64 r; 517 | s64 g; 518 | s64 b; 519 | }; 520 | 521 | struct RGBA64_u 522 | { 523 | u64 r; 524 | u64 g; 525 | u64 b; 526 | u64 a; 527 | }; 528 | 529 | struct RGBA64_s 530 | { 531 | s64 r; 532 | s64 g; 533 | s64 b; 534 | s64 a; 535 | }; 536 | 537 | struct RG64_sfloat 538 | { 539 | double r; 540 | double g; 541 | }; 542 | 543 | struct RGB64_sfloat 544 | { 545 | double r; 546 | double g; 547 | double b; 548 | }; 549 | 550 | struct RGBA64_sfloat 551 | { 552 | double r; 553 | double g; 554 | double b; 555 | double a; 556 | }; 557 | 558 | bitfield R11_unorm_pack16 559 | { 560 | padding: 5; 561 | r : 11; 562 | }; 563 | 564 | ////////////////////////////////////////////////////////////////////////// 565 | // In a couple of places, we'd like to visualize data that can take 566 | // a variety of different formats. A function that defines several 567 | // arrays doesn't quite seem to work, so here's another solution 568 | // (though it's not very elegant!). We define a large macro, 569 | // DATA_ARRAY, that includes the if/else statement over the different 570 | // formats. Macros end at newlines -- they don't support the line 571 | // separator \ -- so DATA_ARRAY is one line. The preprocessor doesn't 572 | // have function macros as of this writing, so we store parameters in 573 | // global variables. The following CommonHeader struct makes this a 574 | // bit easier: when .bary standard properties have an Info object, 575 | // its first 16 bytes always follow the layout of this struct: 576 | struct CommonInfo 577 | { 578 | Format elementFormat; 579 | u32 elementCount; 580 | u32 elementByteSize; 581 | u32 elementByteAlignment; 582 | } [[hidden]]; 583 | // So we can write a function that reads from this struct and sets 584 | // up the necessary parameters for DATA_ARRAY. 585 | 586 | Format g_format; 587 | u128 g_startByte; 588 | u128 g_byteLength; 589 | fn dataArrayPrepare(Format format, u128 byteOffset, u128 elementByteAlignment, u128 structSize, u128 byteLength) 590 | { 591 | g_format = format; 592 | g_startByte = byteOffset + std::mem::align_to(elementByteAlignment, structSize); 593 | g_byteLength = byteLength; 594 | }; 595 | 596 | fn dataArrayPrepareC(CommonInfo commonInfo, u128 byteOffset, u128 structSize) 597 | { 598 | dataArrayPrepare(commonInfo.elementFormat, // 599 | byteOffset, // 600 | commonInfo.elementByteAlignment, // 601 | structSize, // 602 | commonInfo.elementByteSize * commonInfo.elementCount); 603 | }; 604 | 605 | #define DATA_ARRAY if(g_format == Format::eR8_unorm || g_format == Format::eR8_uint){u8 data[clampArray(g_byteLength)] @ g_startByte;} else if(g_format == Format::eR8_snorm || g_format == Format::eR8_sint){s8 data[clampArray(g_byteLength)] @ g_startByte;} else if(g_format == Format::eRG8_unorm || g_format == Format::eRG8_uint){RG8_u data[clampArray(g_byteLength)/2] @ g_startByte;} else if(g_format == Format::eRG8_snorm || g_format == Format::eRG8_sint){RG8_s data[clampArray(g_byteLength)/2] @ g_startByte;} else if(g_format == Format::eRGB8_unorm || g_format == Format::eRGB8_uint){RGB8_u data[clampArray(g_byteLength)/3] @ g_startByte;} else if(g_format == Format::eRGB8_snorm || g_format == Format::eRGB8_sint){RGB8_s data[clampArray(g_byteLength)/3] @ g_startByte;} else if(g_format == Format::eRGBA8_unorm || g_format == Format::eRGBA8_uint){RGBA8_u data[clampArray(g_byteLength)/4] @ g_startByte;} else if(g_format == Format::eRGBA8_snorm || g_format == Format::eRGBA8_sint){RGBA8_s data[clampArray(g_byteLength)/4] @ g_startByte;} else if(g_format == Format::eR16_unorm || g_format == Format::eR16_uint){u16 data[clampArray(g_byteLength)/2] @ g_startByte;} else if(g_format == Format::eR16_snorm || g_format == Format::eR16_sint){s16 data[clampArray(g_byteLength)/2] @ g_startByte;} else if(g_format == Format::eR16_sfloat){R16_sfloat data[clampArray(g_byteLength)/2] @ g_startByte;} else if(g_format == Format::eRG16_unorm || g_format == Format::eRG16_uint){RG16_u data[clampArray(g_byteLength)/4] @ g_startByte;} else if(g_format == Format::eRG16_snorm || g_format == Format::eRG16_sint){RG16_s data[clampArray(g_byteLength)/4] @ g_startByte;} else if(g_format == Format::eRG16_sfloat){RG16_sfloat data[clampArray(g_byteLength)/4] @ g_startByte;} else if(g_format == Format::eRGB16_unorm || g_format == Format::eRGB16_uint){RGB16_u data[clampArray(g_byteLength)/6] @ g_startByte;} else if(g_format == Format::eRGB16_snorm || g_format == Format::eRGB16_sint){RGB16_s data[clampArray(g_byteLength)/6] @ g_startByte;} else if(g_format == Format::eRGB16_sfloat){RGB16_sfloat data[clampArray(g_byteLength)/6] @ g_startByte;} else if(g_format == Format::eRGBA16_unorm || g_format == Format::eRGBA16_uint){RGBA16_u data[clampArray(g_byteLength)/8] @ g_startByte;} else if(g_format == Format::eRGBA16_snorm || g_format == Format::eRGBA16_sint){RGBA16_s data[clampArray(g_byteLength)/8] @ g_startByte;} else if(g_format == Format::eRGBA16_sfloat){RGBA16_sfloat data[clampArray(g_byteLength)/8] @ g_startByte;} else if(g_format == Format::eR32_uint){u32 data[clampArray(g_byteLength)/4] @ g_startByte;} else if(g_format == Format::eR32_sint){s32 data[clampArray(g_byteLength)/4] @ g_startByte;} else if(g_format == Format::eR32_sfloat){float data[clampArray(g_byteLength)/4] @ g_startByte;} else if(g_format == Format::eRG32_uint){RG32_u data[clampArray(g_byteLength)/8] @ g_startByte;} else if(g_format == Format::eRG32_sint){RG32_s data[clampArray(g_byteLength)/8] @ g_startByte;} else if(g_format == Format::eRG32_sfloat){RG32_sfloat data[clampArray(g_byteLength)/8] @ g_startByte;} else if(g_format == Format::eRGB32_uint){RGB32_u data[clampArray(g_byteLength)/12] @ g_startByte;} else if(g_format == Format::eRGB32_sint){RGB32_s data[clampArray(g_byteLength)/12] @ g_startByte;} else if(g_format == Format::eRGB32_sfloat){RGB32_sfloat data[clampArray(g_byteLength)/12] @ g_startByte;} else if(g_format == Format::eRGBA32_uint){RGBA32_u data[clampArray(g_byteLength)/16] @ g_startByte;} else if(g_format == Format::eRGBA32_sint){RGBA32_s data[clampArray(g_byteLength)/16] @ g_startByte;} else if(g_format == Format::eRGBA32_sfloat){RGBA32_sfloat data[clampArray(g_byteLength)/16] @ g_startByte;} else if(g_format == Format::eR64_uint){u64 data[clampArray(g_byteLength)/8] @ g_startByte;} else if(g_format == Format::eR64_sint){s64 data[clampArray(g_byteLength)/8] @ g_startByte;} else if(g_format == Format::eR64_sfloat){double data[clampArray(g_byteLength)/8] @ g_startByte;} else if(g_format == Format::eRG64_uint){RG64_u data[clampArray(g_byteLength)/16] @ g_startByte;} else if(g_format == Format::eRG64_sint){RG64_s data[clampArray(g_byteLength)/16] @ g_startByte;} else if(g_format == Format::eRG64_sfloat){RG64_sfloat data[clampArray(g_byteLength)/16] @ g_startByte;} else if(g_format == Format::eRGB64_uint){RGB64_u data[clampArray(g_byteLength)/24] @ g_startByte;} else if(g_format == Format::eRGB64_sint){RGB64_s data[clampArray(g_byteLength)/24] @ g_startByte;} else if(g_format == Format::eRGB64_sfloat){RGB64_sfloat data[clampArray(g_byteLength)/24] @ g_startByte;} else if(g_format == Format::eRGBA64_uint){RGBA64_u data[clampArray(g_byteLength)/32] @ g_startByte;} else if(g_format == Format::eRGBA64_sint){RGBA64_s data[clampArray(g_byteLength)/32] @ g_startByte;} else if(g_format == Format::eRGBA64_sfloat){RGBA64_sfloat data[clampArray(g_byteLength)/32] @ g_startByte;} else if(g_format == Format::eR11_unorm_pack16){R11_unorm_pack16 data[clampArray(g_byteLength)/2] @ g_startByte;} else {u8 rawData[clampArray(g_byteLength)] @ g_startByte;} 606 | 607 | struct ValuesInfo 608 | { 609 | Format valueFormat; 610 | // spatial layout of values across the subdivided triangle 611 | ValueLayout valueLayout; 612 | // per-vertex or per-triangle 613 | ValueFrequency valueFrequency; 614 | // how many values there are in total (or bytes for compressed / special packed formats) 615 | uint32_t valueCount; 616 | // compressed or special packed formats must use 617 | // valueByteSize 1 618 | uint32_t valueByteSize; 619 | // valueByteAlignment must be at least 4 bytes, higher alignment only 620 | // if it is power of two and either matching valueByteSize, or if special formats 621 | // demand for it. (e.g. eRG32_sfloat is 8 byte aligned, but eRGB32_sfloat 4 byte) 622 | uint32_t valueByteAlignment; 623 | // followed by padding (if required) then values data 624 | }; 625 | ValuesInfo g_sizeValuesInfo; 626 | 627 | struct Group 628 | { 629 | // first/count must be linear ascending and non-overlapping 630 | 631 | uint32_t triangleFirst; 632 | uint32_t triangleCount; 633 | uint32_t valueFirst; 634 | uint32_t valueCount; 635 | 636 | uint32_t minSubdivLevel; 637 | uint32_t maxSubdivLevel; 638 | 639 | // for UNORM,SNORM,FLOAT values these 640 | // represent the final value range 641 | // (value * scale) + bias 642 | ValueFloatVector floatBias; 643 | ValueFloatVector floatScale; 644 | }; 645 | Group g_sizeGroup; // We use sizeof(g_size...) because sizeof(Group) isn't currently allowed in the pattern syntax. 646 | 647 | union BlockFormat 648 | { 649 | uint16_t blockFormat; 650 | BlockFormatDispC1 blockFormatDispC1; 651 | BlockFormatOpaC1 blockFormatOpaC1; 652 | }; 653 | 654 | struct Triangle 655 | { 656 | // valuesOffset must be ascending from t to t+1 657 | // and are relative to the group that this triangle belongs to 658 | // for uncompressed: serves as indexOffset (valueFormat agnostic) 659 | // for compressed / special packed: serves as byteOffset (given valueByteSize is 1) 660 | uint32_t valuesOffset; 661 | uint16_t subdivLevel; 662 | BlockFormat blockFormat; 663 | }; 664 | Triangle g_sizeTriangle; 665 | 666 | struct TriangleMinMaxsInfo 667 | { 668 | // {min, max} value pairs per triangle 669 | // format must always be uncompressed 670 | Format elementFormat; 671 | // count must be 2 x triangle count 672 | uint32_t elementCount; 673 | uint32_t elementByteSize; 674 | uint32_t elementByteAlignment; 675 | // followed by padding (if required) then values data 676 | }; 677 | TriangleMinMaxsInfo g_sizeTriangleMinMaxsInfo; 678 | 679 | struct TriangleUncompressedMip 680 | { 681 | // The element offset of this triangle's first value in the 682 | // UncompressedMipsInfo values array, relative to the start of the group. 683 | // This is equivalent to a byte offset of elementByteSize * mipOffset bytes. 684 | // Can be ~0 if this triangle doesn't need/have a special mip block; 685 | // otherwise, must be ascending from triangle t to t+1. 686 | uint32_t mipOffset; 687 | uint32_t subdivLevel; 688 | }; 689 | TriangleUncompressedMip g_sizeTriangleUncompressedMip; 690 | 691 | struct GroupUncompressedMip 692 | { 693 | // The element offset of this group's first value in the 694 | // UncompressedMipsInfo values array. This is equivalent to a byte offset 695 | // of elementByteSize * mipFirst bytes. 696 | uint32_t mipFirst; 697 | // The number of UncompressedMipsInfo values in this group. This group 698 | // spans elements mipFirst to (but not including) mipFirst + mipCount. 699 | uint32_t mipCount; 700 | }; 701 | GroupUncompressedMip g_sizeGroupUncompressedMip; 702 | 703 | struct UncompressedMipsInfo 704 | { 705 | // if valueFormat == eDispC1_r11_unorm_block then format must be eR11_unorm_packed_align32 706 | Format elementFormat; 707 | uint32_t elementCount; 708 | uint32_t elementByteSize; 709 | uint32_t elementByteAlignment; 710 | // followed by padding (if required) then values data 711 | }; 712 | UncompressedMipsInfo g_sizeUncompressedMipsInfo; 713 | 714 | struct HistogramEntry 715 | { 716 | // The histogram provides detailed usages wihtin compressed files 717 | // to other processing steps and avoids iterating all triangles 718 | // manually. 719 | // 720 | // Each entry stores how many bary triangles are used 721 | // with a unique pairing of block format 722 | // and subdivision level. 723 | uint32_t count; 724 | uint32_t subdivLevel; 725 | BlockFormat blockFormat; 726 | padding[2]; 727 | }; 728 | HistogramEntry g_sizeHistogramEntry; 729 | 730 | struct GroupHistogramRange 731 | { 732 | // into StandardPropertyType::eHistogramEntries 733 | // which histogram entries are valid for this group 734 | uint32_t entryFirst; 735 | uint32_t entryCount; 736 | }; 737 | GroupHistogramRange g_sizeGroupHistogramRange; 738 | 739 | struct MeshDisplacementDirectionsInfo 740 | { 741 | // eR32G32B32_sfloat or eR16G16B16A16_sfloat 742 | // per-vertex displacement directions (linearly interpolated, without normalization) 743 | // if omitted the mesh vertex normals are to be used 744 | Format elementFormat; 745 | uint32_t elementCount; 746 | uint32_t elementByteSize; 747 | uint32_t elementByteAlignment; 748 | // followed by padding (if required) then values data 749 | }; 750 | MeshDisplacementDirectionsInfo g_sizeMeshDisplacementDirectionsInfo; 751 | 752 | struct MeshDisplacementDirectionBoundsInfo 753 | { 754 | // eR32G32_sfloat or eR16G16_sfloat 755 | // per-vertex {bias,scale} 756 | // to maximize the quantization of displacement values, a per-vertex 757 | // {bias,scale} allows to define the shell for the unsigned normalized displacements. 758 | // 759 | // displaced_position = interpolated(vertex_position + vertex_displacement_direction * bounds_bias) + 760 | // interpolated(vertex_displacement_direction * bounds_scale) * displacement_value; 761 | // 762 | // `interpolated` stands for the linear barycentric interpolation of those resulting vertex values of the triangle 763 | // 764 | // If direction bounds are used, Group::floatBias must be 0 and Group::floatScale 1.0 765 | 766 | Format elementFormat; 767 | uint32_t elementCount; 768 | uint32_t elementByteSize; 769 | uint32_t elementByteAlignment; 770 | // followed by padding (if required) then values data 771 | }; 772 | MeshDisplacementDirectionBoundsInfo g_sizeMeshDisplacementDirectionBoundsInfo; 773 | 774 | struct MeshGroup 775 | { 776 | uint32_t triangleFirst; 777 | uint32_t triangleCount; 778 | uint32_t vertexFirst; 779 | uint32_t vertexCount; 780 | }; 781 | MeshGroup g_sizeMeshGroup; 782 | 783 | struct MeshTriangleMappingsInfo 784 | { 785 | // eR32_uint or eR16_uint 786 | Format elementFormat; 787 | uint32_t elementCount; 788 | uint32_t elementByteSize; 789 | uint32_t elementByteAlignment; 790 | // followed by padding (if required) then values data 791 | }; 792 | MeshGroup g_sizeMeshTriangleMappingsInfo; 793 | 794 | struct MeshTriangleFlagsInfo 795 | { 796 | // eR8_uint 797 | // special per mesh triangle flags: 798 | // - currently 1 bit per edge, if set means the neighboring triangle of that edge 799 | // has one subdivision level less. 800 | // Edge order for the triangle (v0,v1,v2) is (v0,v1) (v1,v2) (v2,v0) 801 | Format elementFormat; 802 | uint32_t elementCount; 803 | uint32_t elementByteSize; 804 | uint32_t elementByteAlignment; 805 | // followed by padding (if required) then values data 806 | }; 807 | MeshTriangleFlagsInfo g_sizeMeshTriangleFlagsInfo; 808 | 809 | struct MeshPositionsInfo 810 | { 811 | // eR32G32B32_sfloat or eR16G16B16A16_sfloat 812 | Format elementFormat; 813 | uint32_t elementCount; 814 | uint32_t elementByteSize; 815 | uint32_t elementByteAlignment; 816 | // followed by padding (if required) then values data 817 | }; 818 | MeshPositionsInfo g_sizeMeshPositionsInfo; 819 | 820 | struct MeshTriangleIndicesInfo 821 | { 822 | // eR32_uint or eR16_uint 823 | // 3 indices per triangle 824 | Format elementFormat; 825 | uint32_t elementCount; 826 | uint32_t elementByteSize; 827 | uint32_t elementByteAlignment; 828 | // followed by padding (if required) then values data 829 | }; 830 | MeshTriangleIndicesInfo g_sizeMeshTriangleIndicesInfo; 831 | 832 | struct PropertyInfo 833 | { 834 | // UUID for a property, every new property or every time the 835 | // content definition of a property changes, its identifier must as well. 836 | UUIDLo identifier; 837 | UUIDHi identifierHi; 838 | // byte range must be after the header, and within 839 | // byteSize of header. 840 | ByteRange range; 841 | 842 | SupercompressionScheme supercompressionScheme; 843 | // if `supercompressionScheme` is != SupercompressionScheme::eNone 844 | // then 'range.byteLength` is the supercompressed size and 845 | // this value provides the length after decompression. 846 | uint64_t uncompressedByteLength; 847 | // if exists, global data must be 8 byte aligned 848 | // and come after the above primary `range` for this 849 | // property 850 | ByteRange supercompressionGlobalData; 851 | 852 | padding[4]; 853 | 854 | CommonInfo commonInfo @ range.byteOffset; 855 | 856 | if(identifier == UUIDLo::eValues){ 857 | ValuesInfo valuesInfo @ range.byteOffset; 858 | // Look at the Triangle properties to interpret the data! 859 | dataArrayPrepare(valuesInfo.valueFormat, 860 | range.byteOffset, 861 | valuesInfo.valueByteAlignment, 862 | sizeof(g_sizeValuesInfo), 863 | valuesInfo.valueCount * valuesInfo.valueByteSize); 864 | DATA_ARRAY 865 | } 866 | else if(identifier == UUIDLo::eGroups){ 867 | Group groups[clampArray(range.byteLength/sizeof(g_sizeGroup))] @ range.byteOffset; 868 | } 869 | else if(identifier == UUIDLo::eTriangles){ 870 | Triangle triangles[clampArray(range.byteLength/sizeof(g_sizeTriangle))] @ range.byteOffset; 871 | } 872 | else if(identifier == UUIDLo::eTriangleMinMaxs){ 873 | TriangleMinMaxsInfo triangleMinMaxsInfo @ range.byteOffset; 874 | dataArrayPrepareC(commonInfo, range.byteOffset, sizeof(g_sizeTriangleMinMaxsInfo)); 875 | DATA_ARRAY 876 | } 877 | else if(identifier == UUIDLo::eTriangleUncompressedMips){ 878 | TriangleUncompressedMip triangleUncompressedMips[clampArray(range.byteLength/sizeof(g_sizeTriangleUncompressedMip))] @ range.byteOffset; 879 | } 880 | else if(identifier == UUIDLo::eGroupUncompressedMips){ 881 | GroupUncompressedMip groupUncompressedMips[clampArray(range.byteLength/sizeof(g_sizeGroupUncompressedMip))] @ range.byteOffset; 882 | } 883 | else if(identifier == UUIDLo::eUncompressedMips){ 884 | UncompressedMipsInfo uncompressedMipsInfo @ range.byteOffset; 885 | dataArrayPrepareC(commonInfo, range.byteOffset, sizeof(g_sizeUncompressedMipsInfo)); 886 | DATA_ARRAY 887 | } 888 | else if(identifier == UUIDLo::eMeshGroups){ 889 | MeshGroup meshGroups[clampArray(range.byteLength/sizeof(g_sizeMeshGroup))] @ range.byteOffset; 890 | } 891 | else if(identifier == UUIDLo::eHistogramEntries){ 892 | HistogramEntry histogramEntries[clampArray(range.byteLength/sizeof(g_sizeHistogramEntry))] @ range.byteOffset; 893 | } 894 | else if(identifier == UUIDLo::eGroupHistograms){ 895 | GroupHistogramRange groupHistogramRanges[clampArray(range.byteLength/sizeof(g_sizeGroupHistogramRange))] @ range.byteOffset; 896 | } 897 | else if(identifier == UUIDLo::eMeshDisplacementDirections){ 898 | MeshDisplacementDirectionsInfo meshDisplacementDirectionsInfo @ range.byteOffset; 899 | dataArrayPrepareC(commonInfo, range.byteOffset, sizeof(g_sizeMeshDisplacementDirectionsInfo)); 900 | DATA_ARRAY 901 | } 902 | else if(identifier == UUIDLo::eMeshDisplacementDirectionBounds){ 903 | MeshDisplacementDirectionBoundsInfo meshDisplacementDirectionBoundsInfo @ range.byteOffset; 904 | dataArrayPrepareC(commonInfo, range.byteOffset, sizeof(g_sizeMeshDisplacementDirectionBoundsInfo)); 905 | DATA_ARRAY 906 | } 907 | else if(identifier == UUIDLo::eMeshTriangleMappings){ 908 | MeshTriangleMappingsInfo meshTriangleMappingsInfo @ range.byteOffset; 909 | dataArrayPrepareC(commonInfo, range.byteOffset, sizeof(g_sizeMeshTriangleMappingsInfo)); 910 | DATA_ARRAY 911 | } 912 | else if(identifier == UUIDLo::eMeshTriangleFlags){ 913 | MeshTriangleFlagsInfo meshTriangleFlagsInfo @ range.byteOffset; 914 | dataArrayPrepareC(commonInfo, range.byteOffset, sizeof(g_sizeMeshTriangleFlagsInfo)); 915 | DATA_ARRAY 916 | } 917 | else if(identifier == UUIDLo::eMeshPositions){ 918 | MeshPositionsInfo meshPositionsInfo @ range.byteOffset; 919 | dataArrayPrepareC(commonInfo, range.byteOffset, sizeof(g_sizeMeshPositionsInfo)); 920 | DATA_ARRAY 921 | } 922 | else if(identifier == UUIDLo::eMeshTriangleIndices){ 923 | MeshTriangleIndicesInfo meshTriangleIndicesInfo @ range.byteOffset; 924 | dataArrayPrepareC(commonInfo, range.byteOffset, sizeof(g_sizeMeshTriangleIndicesInfo)); 925 | DATA_ARRAY 926 | } 927 | }; 928 | 929 | ////////////////////////////////////////////////////////////////////////// 930 | // File structure 931 | 932 | Header header @ 0x00; 933 | 934 | if(std::string::substr(header.versionIdentifier, 0, 16) != "\xAB\x42\x41RY 00100\xBB\x0D\x0A\x1A\x0A") 935 | { 936 | std::error(std::format("This isn't a .bary file, or it had an unknown version number! 937 | (All released.bary files should have version number 00100, but this one had version number{}.)", 938 | std::string::substr(header.versionIdentifier, 6, 5))); 939 | } 940 | 941 | PropertyInfo g_forPropertyInfoSize; 942 | PropertyInfo properties[header.propertyInfoRange.byteLength/sizeof(g_forPropertyInfoSize)] @ header.propertyInfoRange.byteOffset; 943 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NVIDIA Displacement Micro-Map BaryFile 2 | 3 | Repository for barycentric data fileformat '.bary' 4 | 5 | `BARY` is an fileformat that serves as container for micromap data. 6 | 7 | Micromaps store per-microvertex or per-microtriangle data for a micromesh 8 | that is the result of evenly subdividing a triangle. 9 | 10 | Each subdivided triangle contains its own set of values, there is 11 | no sharing of values across subdivided triangles. 12 | 13 | ``` 14 | V 15 | 4 16 | / \ 17 | 3 __ 8 18 | / \ / \ 19 | 2 __ 7 __ 11 20 | / \ / \ / \ 21 | 1 __ 6 __ 10 __ 13 22 | / \ / \ / \ / \ 23 | 0 __ 5 __ 9 __ 12 __ 14 24 | W U 25 | 26 | Result of subdivision level 2 for 27 | triangle W,U,V 28 | 29 | 16 microtriangles 30 | 15 microvertices 31 | ``` 32 | 33 | The file format is designed to be directly uploaded to the GPU 34 | and passing data structures into 3D APIs without additional processing. 35 | 36 | ## bary_core library 37 | 38 | This library defines the structs and principle file structure in memory 39 | and uses the `bary` namespace. 40 | 41 | It comes with core API functions that operate on pointers to aid 42 | serialization or validation of the data. No allocations or file IO operations 43 | are performed in this library. 44 | 45 | All content of a bary file is expressed via properties, that are idenfitied 46 | through a v4 UUID. 47 | 48 | ## bary_utils library 49 | 50 | The bary_utils library adds some utility C++ classes that leverage STL containers 51 | and adds basic file loaders and savers using `stdio`. 52 | It makes use of the functions in bary_core to implement many of these utilities 53 | and uses its own namespace `baryutils`. 54 | 55 | By default CMAKE does not build this library and you must opt-in. 56 | 57 | ## New VkFormats for barycentric micromaps 58 | 59 | The primary use in 3D APIs for barycentric data are displacement and opacity micromaps. 60 | Displacement can be block-compressed similar to BC/ASTC. 61 | 62 | However, any other scalar values can be stored in bary files for exchange, 63 | there just isn't a native 3D API use for them. 64 | 65 | `BARY` uses VkFormat enum values, we reserved two Vk extensions (397 and 398) to get the value ranges below. 66 | 67 | **! NOTE: These could change, rely only what is provided in the headers for now !** 68 | 69 | ``` c++ 70 | // bary::Format uses VkFormat values 71 | enum class Format : uint32_t { 72 | ... 73 | // opacity encoding (based on VK NV extension reservation 397) 74 | 75 | // block-compressed opacity format 76 | // for uncompressed 1 or 2 bit data stored in 8-bit 77 | // valueByteSize = 1 78 | eOpaC1_rx_uint_block = 1000396000, 79 | 80 | // displacement encoding (based on VK NV extension reservation 398) 81 | 82 | // block-compressed displacement format 83 | // for compressed data stored in blocks of 512- or 1024-bit 84 | // valueByteSize = 1 85 | // valueByteAlignment = 128 86 | // minmax as eR11_unorm_pack16 87 | eDispC1_r11_unorm_block = 1000397000, 88 | 89 | // for uncompressed data 1 x 11 bit stored in 16-bit 90 | eR11_unorm_pack16 = 1000397001, 91 | 92 | // variable packed format 93 | // for uncompressed data 1 x 11 bit densely packed sequence of 32bit values. 94 | // Each triangle starts at a 32-bit boundary. 95 | // valueByteSize = 1 96 | // minmax as eR11_unorm_pack16 97 | eR11_unorm_packed_align32 = 1000397002, 98 | ... 99 | }; 100 | 101 | // encodes 1 or 2 bit opacity maps 102 | enum class BlockFormatOpaC1 : uint16_t 103 | { 104 | eInvalid = 0, 105 | eR1_uint_x8 = 1, 106 | eR2_uint_x4 = 2, 107 | }; 108 | 109 | // encodes displacement maps of unorm11 values 110 | enum class BlockFormatDispC1 : uint16_t 111 | { 112 | eInvalid = 0, 113 | eR11_unorm_lvl3_pack512 = 1, 114 | eR11_unorm_lvl4_pack1024 = 2, 115 | eR11_unorm_lvl5_pack1024 = 3, 116 | }; 117 | ``` 118 | 119 | ## Principle dataflow 120 | 121 | Here are some of the core data structures stored within bary files. 122 | 123 | ``` c++ 124 | // gives details about what values are stored 125 | // and how they are laid out across a triangle. 126 | struct bary::ValuesInfo 127 | { 128 | Format valueFormat; 129 | // spatial layout of values across the subdivided triangle 130 | ValueLayout valueLayout; 131 | // per-vertex or per-triangle 132 | ValueFrequency valueFrequency; 133 | // how many values there are in total (or bytes for compressed / special packed formats) 134 | uint32_t valueCount; 135 | // compressed or special packed formats must use 136 | // valueByteSize 1 137 | uint32_t valueByteSize; 138 | // valueByteAlignment must be at least 4 bytes, higher alignment only 139 | // if it is power of two and either matching valueByteSize, or if special formats 140 | // demand for it. (e.g. eRG32_sfloat is 8 byte aligned, but eRGB32_sfloat 4 byte) 141 | uint32_t valueByteAlignment; 142 | } 143 | 144 | // provides key information for every Micromap Triangle 145 | struct bary::Triangle 146 | { 147 | // valuesOffset must be ascending from t to t+1 148 | // and are relative to the group that this triangle belongs to 149 | // for uncompressed: serves as indexOffset (valueFormat agnostic) 150 | // for compressed / special packed: serves as byteOffset (given valueByteSize is 1 for those) 151 | uint32_t valuesOffset; 152 | // the subdivision level of this triangle, influences how much 153 | // values are relevant to it. 154 | uint16_t subdivLevel; 155 | 156 | // if the values are stored compressed 157 | uint16_t blockFormat; 158 | }; 159 | 160 | // groups allow to store multiple groups of independent values and triangles 161 | // of same value info in one file. 162 | struct bary::Group 163 | { 164 | // first/count must be linear ascending and non-overlapping 165 | 166 | uint32_t triangleFirst; 167 | uint32_t triangleCount; 168 | uint32_t valueFirst; 169 | uint32_t valueCount; 170 | 171 | uint32_t minSubdivLevel; 172 | uint32_t maxSubdivLevel; 173 | 174 | // for UNORM,SNORM,FLOAT values these 175 | // represent the final value range 176 | // (value * scale) + bias 177 | ValueFloatVector floatBias; 178 | ValueFloatVector floatScale; 179 | }; 180 | 181 | // typical file content can therefore be accessed via a pointer view 182 | struct bary::BasicPropsView 183 | { 184 | // mandatory for all 185 | const Group* groups = nullptr; 186 | uint32_t groupsCount = 0; 187 | const ValuesInfo* valuesInfo = nullptr; 188 | const uint8_t* values = nullptr; 189 | const Triangle* triangles = nullptr; 190 | uint32_t trianglesCount = 0; 191 | ... 192 | }; 193 | ``` 194 | The following describes their relationship: 195 | 196 | - `mesh.`: data typically stored within the 3d mesh file loaded by application 197 | - `bary.`: data stored within a bary file 198 | 199 | - `mesh.triangle`: a triangle (triangle/quad) within the geometry (aka baseTriangle), is mapped to 1: 200 | - `bary.triangle`: a triangle within the barycentric data container, has information where actual values are stored within the bary values. Can be split into **N >= 1**: 201 | - `bary.blocktriangle`: when block compression is used, splitting of a `bary.triangle` into triangles of lower subdivision that each represent a compressed block. 202 | 203 | Looking at the data we store in file: 204 | 205 | - `mesh.triangleMapping`: index to map `mesh.triangle` to `bary.triangle` (think like UV coordinate for texture) 206 | The majority of the content will have unique 1:1 mapping, which allows to skip this mapping buffer. This is mostly meant for micro-instancing 207 | some displacements all over within a single mesh. 208 | 209 | - `mesh.triangleFlags`: used to generate watertight displacement for each `mesh.triangle` 210 | - store "half resolution" information, set the n-th bit, if the n-th edge has an adjacent `mesh.triangle` of half resolution `bary.triangle` 211 | - cannot be stored in the bary file, as each `mesh.triangle` could have different adjacency behahvior but map to the same `bary.triangle`. 212 | 213 | - `bary.triangles`: links bary triangles to values. 214 | - subdivision level of triangle 215 | - offsets for values 216 | - block format (if all explicit or implicit subtriangles use the same block format) 217 | 218 | - `bary.values` : raw data uploaded in one big blob to GPU, stores values in special barycentric ordering (uncompressed or compressed formats exist) 219 | 220 | 221 | ``` c++ 222 | for baseTriangleIdx in mesh.triangles 223 | { 224 | tri = bary.triangles[ mesh.barytriangleMapping[ baseTriangleIdx ] ]; 225 | 226 | triValues = & bary.values[ tri.valuesOffset ]; 227 | 228 | // these values are either per vertex or per triangle and stored in a canonical 229 | // spatial order according to the valueLayout 230 | 231 | for (i < computeValueCount(bary.valuesInfo.valueFrequency, tri.subdivLevel)) 232 | { 233 | value = triValues[ i ]; 234 | } 235 | } 236 | ``` 237 | 238 | ### Limitations 239 | 240 | - Barycentric data is dependent on the winding of the mesh triangle. 241 | - Currently no support for "mirrored" barycentric data (same data used with a different `mesh.triangle` winding order) like UV textures would allow. 242 | 243 | ### Open Issues 244 | 245 | - Modelling tools currently will not support generating a mapping table between mesh and bary container. 246 | Modifying such tables under topological changes during modelling operations would also be unfortunate. 247 | For now we focus on a unique 1:1 mapping of mesh and barycentric triangles. 248 | 249 | One relatively easy method to enhance this, is to use the existing three UV-texture coords of a mesh 250 | triangle as a single key to identify which triangles should get matching mapping indices. 251 | This way we can re-use relationship information available in existing files, as well as use existing 252 | UV-tools. We would simply not care about UV-texture coords as such, but only for sake to identify those 253 | triangles that should use same barycentric data. This way an artist can clone the mesh triangles and 254 | the relationship, that these share identical values, would stay intact. 255 | 256 | ## Support Contact 257 | 258 | Feel free to file issues directly on the GitHub page or reach out to NVIDIA at 259 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | ## Security 2 | 3 | NVIDIA is dedicated to the security and trust of our software products and services, including all source code repositories managed through our organization. 4 | 5 | If you need to report a security issue, please use the appropriate contact points outlined below. **Please do not report security vulnerabilities through GitHub/GitLab.** 6 | 7 | ## Reporting Potential Security Vulnerability in an NVIDIA Product 8 | 9 | To report a potential security vulnerability in any NVIDIA product: 10 | - Web: [Security Vulnerability Submission Form](https://www.nvidia.com/object/submit-security-vulnerability.html) 11 | - E-Mail: psirt@nvidia.com 12 | - We encourage you to use the following PGP key for secure email communication: [NVIDIA public PGP Key for communication](https://www.nvidia.com/en-us/security/pgp-key) 13 | - Please include the following information: 14 | - Product and version/branch that contains the vulnerability 15 | - Type of vulnerability (code execution, denial of service, buffer overflow, etc.) 16 | - Instructions to reproduce the vulnerability 17 | - Proof-of-concept or exploit code 18 | - Potential impact of the vulnerability, including how an attacker could exploit the vulnerability 19 | 20 | While NVIDIA currently does not have a bug bounty program, we do offer acknowledgement when an externally reported security issue is addressed under our coordinated vulnerability disclosure policy. Please visit our [Product Security Incident Response Team (PSIRT)](https://www.nvidia.com/en-us/security/psirt-policies/) policies page for more information. 21 | 22 | ## NVIDIA Product Security 23 | 24 | For all security-related concerns, please visit NVIDIA's Product Security portal at https://www.nvidia.com/en-us/security -------------------------------------------------------------------------------- /fuzz/bary_fuzz.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | // Implements a libFuzzer target for bary files. This provides an automated 19 | // way we can check bary_core is safe when trying to open untrusted .bary files. 20 | // For more information about how to run a libFuzzer target, please see 21 | // https://llvm.org/docs/LibFuzzer.html or 22 | // https://github.com/google/fuzzing/blob/master/tutorial/libFuzzerTutorial.md. 23 | 24 | #include 25 | #include 26 | #include 27 | #include "bary/bary_core.h" 28 | 29 | // LLVM libFuzzer entrypoint. 30 | // libFuzzer generates main() for us automatically, since we compile this using 31 | // -fsanitize=fuzzer. 32 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) 33 | { 34 | // This fuzzer interprets the entire test input as if it was a .bary 35 | // file, and tests if opening and saving it would cause unexpected 36 | // program execution. 37 | // For now, we always allow libFuzzer to add inputs to the corpus 38 | // by returning 0. 39 | 40 | if(bary::Result::eSuccess != bary::baryDataIsValid(Size, Data)) 41 | { 42 | return 0; 43 | } 44 | 45 | bary::ContentView content{}; 46 | bary::StandardPropertyType errorProperty{}; 47 | if(bary::Result::eSuccess != bary::baryDataGetContent(Size, Data, &content, &errorProperty)) 48 | { 49 | return 0; 50 | } 51 | 52 | if(bary::Result::eSuccess != bary::baryContentIsValid(bary::ValueSemanticType::eDisplacement, &content, &errorProperty)) 53 | { 54 | return 0; 55 | } 56 | 57 | // Get all the properties, and try to access their first and last elements. 58 | // If a property somehow has a range that is not valid, halt: it must have 59 | // somehow slipped past the bary validator. 60 | uint64_t numProperties = 0; 61 | const bary::PropertyInfo* properties = bary::baryDataGetAllPropertyInfos(Size, Data, &numProperties); 62 | for(uint64_t i = 0; i < numProperties; i++) 63 | { 64 | if(!bary::baryDataIsRangeValid(Size, properties[i].range)) 65 | { 66 | abort(); 67 | } 68 | if(!bary::baryDataIsRangeValid(Size, properties[i].supercompressionGlobalData)) 69 | { 70 | abort(); 71 | } 72 | } 73 | 74 | // Now, let's emulate an app that has loaded the .bary file into internal 75 | // structures, and is now trying to save the .bary file back out. 76 | if(numProperties > std::numeric_limits::max()) 77 | { 78 | return 0; 79 | } 80 | 81 | std::vector propertyStorageInfos; 82 | try 83 | { 84 | for(uint64_t i = 0; i < numProperties; i++) 85 | { 86 | bary::PropertyStorageInfo psi{}; 87 | psi.identifier = properties[i].identifier; 88 | psi.dataSize = properties[i].range.byteLength; 89 | psi.data = Data + properties[i].range.byteOffset; 90 | psi.supercompressionScheme = properties[i].supercompressionScheme; 91 | psi.uncompressedSize = properties[i].uncompressedByteLength; 92 | psi.supercompressionGlobalDataSize = properties[i].supercompressionGlobalData.byteLength; 93 | psi.supercompressionGlobalData = Data + properties[i].supercompressionGlobalData.byteOffset; 94 | propertyStorageInfos.push_back(psi); 95 | } 96 | } 97 | catch(const std::bad_alloc& /* unused */) 98 | { 99 | return 0; 100 | } 101 | 102 | if(bary::Result::eSuccess 103 | != bary::baryValidateStandardProperties(static_cast(numProperties), propertyStorageInfos.data(), 104 | bary::ValidationFlagBit::eValidationFlagArrayContents 105 | | bary::ValidationFlagBit::eValidationFlagTriangleValueRange, 106 | &errorProperty)) 107 | { 108 | return 0; 109 | } 110 | 111 | const uint64_t outputSize = bary::baryStorageComputeSize(static_cast(numProperties), propertyStorageInfos.data()); 112 | std::vector outputData; 113 | try 114 | { 115 | outputData.resize(outputSize); 116 | } 117 | catch(const std::bad_alloc& /* unused */) 118 | { 119 | return 0; 120 | } 121 | 122 | if(bary::Result::eSuccess 123 | != bary::baryStorageOutputAll(static_cast(numProperties), propertyStorageInfos.data(), outputSize, 124 | outputData.data())) 125 | { 126 | return 0; 127 | } 128 | 129 | return 0; 130 | } 131 | -------------------------------------------------------------------------------- /fuzz/bary_fuzz.dict: -------------------------------------------------------------------------------- 1 | # BARY 1.0 magic number 2 | "\xABBARY 00100\xBB\x0D\x0A\x1A\x0A" 3 | 4 | # Special formats for opacity and displacement, in little-endian 5 | "\xE0\xD4\xA0\x3B" 6 | "\xC8\xD8\xA0\x3B" 7 | "\xC9\xD8\xA0\x3B" 8 | "\xCA\xD8\xA0\x3B" 9 | 10 | # 16-byte standard property identifiers. 11 | "\x04\xAA\x4D\xB4\xD5\x44\xE0\xC9\xE0\x4D\x94\x9A\x35\xFE\xD8\xCF" 12 | "\xD0\x40\xEE\x39\x17\x45\xC4\x9D\x5D\xB1\x5A\x8E\xBC\x74\x9C\xB0" 13 | "\x68\x8E\x45\x00\x6C\x42\x59\xEE\x7F\x1B\xBF\xB3\x8E\xEB\x9D\x74" 14 | "\xC1\x6B\x75\x6E\x8A\x43\x9A\x83\x5E\x74\x4A\xAD\x51\x38\x6C\x55" 15 | "\x2F\x97\x0D\x40\x37\x43\xDD\x76\xE9\xC6\x00\x99\xFE\x6F\xC4\xC9" 16 | "\xDA\xEB\xBE\x94\xF0\x47\xC3\x7E\x0E\xDE\xF1\xA5\x1B\x85\xBB\xCF" 17 | "\xE0\x84\xCE\x68\xF3\x48\x44\x7F\x6E\xBA\x58\xBF\xD0\xCF\xD8\x08" 18 | "\x98\x78\xA3\x02\x56\x40\x56\x74\xD7\xBE\xB1\x93\x6E\x38\x05\x7F" 19 | "\x87\xD6\x62\xF2\xEB\x4A\x28\xB9\x3C\x80\x06\xA7\x52\xAE\xED\xCB" 20 | "\x65\x3C\xBF\x25\xE1\x4A\x23\x29\x3C\xE4\xEF\x95\x6C\x06\x87\xEB" 21 | "\xD0\x3A\xDC\x9C\x8A\x4A\xBB\x92\x59\x87\x65\xB4\x54\x1D\xD4\x42" 22 | "\xD3\xEE\xF9\x90\x74\x49\xC3\x4E\x5C\x75\x0C\x97\xA3\x53\x5B\xAF" 23 | "\xFE\x1C\x07\xAC\x0D\x43\x1D\xC0\x22\xF8\x6F\x93\x8E\xB4\xD6\xA2" 24 | "\xDB\x06\xF1\x48\x0F\x41\xAF\x1D\x5C\xC3\xF1\x8C\x09\x93\x55\x69" 25 | "\x06\x07\x01\x23\xB7\x4E\x74\x56\xED\x6C\x0D\x8C\xF9\xD2\x38\x51" 26 | "\x1C\xDF\xB5\xD8\xEF\x41\x2B\xCC\xDF\xB4\xFB\x84\x14\x0A\xF1\xBF" 27 | "\x46\x49\x09\x57\x03\x46\x7B\xC4\xE8\xB6\xB8\x8C\x27\xCD\x21\x62" 28 | "\xE6\x66\x5A\x58\x23\x44\x33\xCB\x57\x13\x82\xBE\x52\xF5\x71\xC4" -------------------------------------------------------------------------------- /include/bary/bary_api.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #pragma once 19 | 20 | #if defined(_MSC_VER) 21 | #define BARY_CALL __fastcall 22 | #elif !defined(__aarch64__) && !defined(__x86_64) && (defined(__GNUC__) || defined(__clang__)) 23 | #define BARY_CALL __attribute__((fastcall)) 24 | #else 25 | #define BARY_CALL 26 | #endif 27 | 28 | // anticipate dll etc. 29 | 30 | #ifndef BARY_API 31 | #define BARY_API extern "C" 32 | #endif -------------------------------------------------------------------------------- /include/bary/bary_core.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | #pragma once 18 | 19 | #include "bary_types.h" 20 | #include "bary_api.h" 21 | 22 | namespace bary 23 | { 24 | ////////////////////////////////////////////// 25 | 26 | BARY_API VersionIdentifier BARY_CALL baryGetCurrentVersionIdentifier(); 27 | 28 | BARY_API Result baryVersionIdentifierGetVersion(const VersionIdentifier* identifier, uint32_t* pVersion); 29 | 30 | ////////////////////////////////////////////// 31 | 32 | // get min/max across all groups, pointers are optional 33 | BARY_API void BARY_CALL baryBasicViewGetMinMaxSubdivLevels(const BasicView* basic, uint32_t* min, uint32_t* max); 34 | 35 | BARY_API uint32_t BARY_CALL baryContentComputePropertyCount(const ContentView* content); 36 | 37 | // only tests non-generic semantic related validation, not full validation 38 | BARY_API Result BARY_CALL baryContentIsValid(ValueSemanticType valueSemantic, const ContentView* content, StandardPropertyType* errorPropertyType); 39 | 40 | BARY_API Result BARY_CALL baryContentSetupProperties(const ContentView* content, uint32_t propertyCount, PropertyStorageInfo* propertyStorageInfos); 41 | 42 | 43 | // content structs point to section of fileData 44 | // mesh and misc are optional 45 | // only valid for non-supercompressed data 46 | BARY_API Result BARY_CALL baryDataGetContent(uint64_t fileSize, const void* fileData, ContentView* content, StandardPropertyType* errorPropertyType); 47 | 48 | ////////////////////////////////////////////// 49 | 50 | BARY_API const char* BARY_CALL baryResultGetName(Result result); 51 | 52 | ////////////////////////////////////////////// 53 | 54 | BARY_API const char* BARY_CALL baryFormatGetName(Format format); 55 | 56 | ////////////////////////////////////////////// 57 | 58 | BARY_API PropertyIdentifier BARY_CALL baryStandardPropertyGetIdentifier(StandardPropertyType type); 59 | 60 | BARY_API const char* BARY_CALL baryStandardPropertyGetName(StandardPropertyType type); 61 | 62 | // returns zero if unknown 63 | BARY_API uint32_t BARY_CALL baryStandardPropertyGetElementSize(StandardPropertyType type); 64 | 65 | // returns zero if unknown or no info 66 | BARY_API uint32_t BARY_CALL baryStandardPropertyGetInfoSize(StandardPropertyType type); 67 | 68 | ////////////////////////////////////////////// 69 | 70 | inline bool baryPropertyIsEqual(PropertyIdentifier a, PropertyIdentifier b) 71 | { 72 | return a.uuid4[0] == b.uuid4[0] && a.uuid4[1] == b.uuid4[1] && a.uuid4[2] == b.uuid4[2] && a.uuid4[3] == b.uuid4[3]; 73 | } 74 | 75 | BARY_API StandardPropertyType BARY_CALL baryPropertyGetStandardType(PropertyIdentifier identifier); 76 | 77 | // will also return 0 if not a standard property 78 | // derives group/triangle count etc. from standard properties 79 | inline uint64_t baryPropertyGetStandardElementCount(PropertyIdentifier identifier, ByteRange range) 80 | { 81 | uint64_t elementSize = baryStandardPropertyGetElementSize(baryPropertyGetStandardType(identifier)); 82 | return elementSize ? range.byteLength / elementSize : 0; 83 | } 84 | 85 | 86 | ////////////////////////////////////////////////////////////////////////// 87 | 88 | // return padding size for a property that leads with an info struct 89 | // and is followed by payload with a certain alignment requirement 90 | inline uint64_t baryPayloadGetPadding(uint64_t infoStructSize, uint32_t payloadByteAlignment) 91 | { 92 | if(payloadByteAlignment <= 1) 93 | return 0; 94 | uint64_t rest = infoStructSize % payloadByteAlignment; 95 | return rest ? uint64_t(payloadByteAlignment) - rest : 0; 96 | } 97 | 98 | // return (info size + padding size) for a property that leads with an info struct 99 | // and is followed by payload with a certain alignment requirement 100 | inline uint64_t baryPayloadGetOffset(uint64_t infoStructSize, uint32_t payloadByteAlignment) 101 | { 102 | if(payloadByteAlignment <= 1) 103 | return infoStructSize; 104 | uint64_t rest = infoStructSize % payloadByteAlignment; 105 | return infoStructSize + (rest ? uint64_t(payloadByteAlignment) - rest : 0); 106 | } 107 | 108 | // if a property leads with an info struct and then comes payload 109 | // return the payload start pointer location. 110 | // can account for special padding between info and payload. 111 | inline const void* baryPayloadGetPointer(uint64_t infoStructSize, uint32_t payloadByteAlignment, const void* ptr) 112 | { 113 | uint64_t payloadOffset = baryPayloadGetOffset(infoStructSize, payloadByteAlignment); 114 | 115 | return reinterpret_cast(ptr) + payloadOffset; 116 | } 117 | 118 | // if a property leads with an info struct and then comes payload 119 | // return the payload start pointer location. 120 | // can account for special padding between info and payload. 121 | inline void* baryPayloadGetPointer(uint64_t infoStructSize, uint32_t payloadByteAlignment, void* ptr) 122 | { 123 | uint64_t payloadOffset = baryPayloadGetOffset(infoStructSize, payloadByteAlignment); 124 | 125 | return reinterpret_cast(ptr) + payloadOffset; 126 | } 127 | 128 | ////////////////////////////////////////////// 129 | 130 | inline bool baryPropertyStorageHasValidSize(const PropertyStorageInfo& prop, uint64_t expectedInfoSize) 131 | { 132 | if(!prop.infoSize) 133 | { 134 | return prop.dataSize >= (expectedInfoSize); 135 | } 136 | else 137 | { 138 | return (prop.infoSize == expectedInfoSize); 139 | } 140 | } 141 | 142 | inline bool baryPropertyStorageHasValidPadding(const PropertyStorageInfo& prop, uint64_t expectedInfoSize, uint64_t paddingAlignment) 143 | { 144 | uint64_t padding = baryPayloadGetPadding(expectedInfoSize, uint32_t(paddingAlignment)); 145 | 146 | if(!prop.infoSize) 147 | { 148 | return prop.dataSize >= (expectedInfoSize + padding); 149 | } 150 | else 151 | { 152 | return (prop.infoSize == expectedInfoSize && prop.infoPaddingSize == padding); 153 | } 154 | } 155 | 156 | ////////////////////////////////////////////// 157 | 158 | inline bool baryUVisValid(BaryUV_uint16 coord, uint32_t subdiv) 159 | { 160 | uint32_t max = 1u << subdiv; 161 | return (coord.u <= max && coord.v <= max && uint32_t(coord.u + coord.v) <= max); 162 | } 163 | 164 | 165 | BARY_API const char* BARY_CALL baryValueLayoutGetName(ValueLayout layout); 166 | 167 | // returns ~0 if invalid combination 168 | // isUpperTriangle means the triangle in a quad with lower left corner at u,v (see below) 169 | // 170 | // x___x 171 | // |0\1| number inside triangle reflects isUpperTriangle value for per_triangle 172 | // uv__x 173 | // 174 | BARY_API uint32_t BARY_CALL 175 | baryValueLayoutGetIndex(ValueLayout order, ValueFrequency frequency, uint32_t u, uint32_t v, uint32_t isUpperTriangle, uint32_t subdivLevel); 176 | 177 | // generates a UV mesh for the provided ValueOrder 178 | // triangles and vertices ordered accordingly 179 | // pUVs == 2 x uint16_t x uvCount 180 | // pTriangleIndices == 3 x uint32_t x triangleCount 181 | // the use of edgeDecimateFlag can cause degenerate 182 | // triangles and unreferenced vertices 183 | BARY_API Result BARY_CALL baryValueLayoutGetUVMesh(ValueLayout order, 184 | uint32_t subdivLevel, 185 | uint32_t edgeDecimateFlag, 186 | uint32_t uvCount, 187 | BaryUV_uint16* pUVs, 188 | uint32_t triangleCount, 189 | uint32_t* pTriangleIndices); 190 | 191 | // for a micro-vertex stored in bird-order retrieve details about its subdivLevel and index within the level 192 | BARY_API void BARY_CALL baryBirdLayoutGetVertexLevelInfo(uint32_t u, uint32_t v, uint32_t subdivLevel, uint32_t* outLevel, uint32_t* outLevelCoordIndex); 193 | 194 | 195 | BARY_API const char* BARY_CALL baryValueFrequencyGetName(ValueFrequency frequency); 196 | 197 | // Computes how many values are stored for a certain frequency and subdivLevel. 198 | // If the ValueFrequency is unknown or if integer overflow would occur, returns 0. 199 | inline uint32_t baryValueFrequencyGetCount(ValueFrequency frequency, uint32_t subdivLevel) 200 | { 201 | switch(frequency) 202 | { 203 | case ValueFrequency::ePerVertex: { 204 | if(subdivLevel > 15) 205 | return 0; 206 | uint32_t numVertsPerEdge = (1u << subdivLevel) + 1; 207 | return (numVertsPerEdge * (numVertsPerEdge + 1)) / 2; 208 | } 209 | case ValueFrequency::ePerTriangle: 210 | if(subdivLevel > 15) 211 | return 0; 212 | return 1 << (subdivLevel * 2); 213 | default: 214 | return 0; 215 | } 216 | } 217 | 218 | 219 | ////////////////////////////////////////////////////////////////////////// 220 | // compression related 221 | 222 | inline BaryUV_uint16 baryBlockTriangleLocalToBaseUV(const BlockTriangle* info, BaryUV_uint16 locaUV) 223 | { 224 | int32_t anchor[2] = {info->vertices[0].u, info->vertices[0].v}; 225 | int32_t signs[2] = {info->vertices[1].u > info->vertices[0].u ? 1 : -1, info->vertices[2].v > info->vertices[0].v ? 1 : -1}; 226 | int32_t local[2] = {locaUV.u, locaUV.v}; 227 | local[0] *= signs[0]; 228 | local[1] *= signs[1]; 229 | 230 | BaryUV_uint16 baseUV; 231 | baseUV.u = uint16_t(anchor[0] + local[0] + (signs[0] != signs[1] ? -local[1] : 0)); 232 | baseUV.v = uint16_t(anchor[1] + local[1]); 233 | return baseUV; 234 | } 235 | 236 | // may return invalid / out of bounds coords, use baryUVisValid 237 | inline BaryUV_uint16 baryBlockTriangleBaseToLocalUV(const BlockTriangle* info, BaryUV_uint16 baseUV) 238 | { 239 | int32_t base[2] = {baseUV.u, baseUV.v}; 240 | int32_t anchor[2] = {info->vertices[0].u, info->vertices[0].v}; 241 | int32_t signs[2] = {info->vertices[1].u > info->vertices[0].u ? 1 : -1, info->vertices[2].v > info->vertices[0].v ? 1 : -1}; 242 | int32_t local[2] = {}; 243 | local[1] = base[1] - anchor[1]; 244 | local[0] = base[0] - anchor[0] - (signs[0] != signs[1] ? -local[1] : 0); 245 | 246 | local[0] *= signs[0]; 247 | local[1] *= signs[1]; 248 | 249 | BaryUV_uint16 locaUV; 250 | locaUV.u = uint16_t(local[0]); 251 | locaUV.v = uint16_t(local[1]); 252 | return locaUV; 253 | } 254 | 255 | inline uint32_t baryBlockTriangleBaseToLocalFlags(const BlockTriangle* subSplit, uint32_t baseFlags) 256 | { 257 | uint32_t baseTopo = 0; 258 | uint32_t baseEdgeIndices = subSplit->baseEdgeIndices; 259 | // For each edge of the subprimitive... 260 | for(uint32_t e = 0; e < 3; e++) 261 | { 262 | // Look at the subprim info to determine which base triangle edge this 263 | // refers to. 3 indicates no base triangle edge. 264 | const uint32_t baseEdge = (baseEdgeIndices >> (e * 2)) & 3; 265 | if(baseEdge != 3) 266 | { 267 | const uint32_t baseEdgeFlag = (baseFlags >> baseEdge) & 1; 268 | baseTopo |= baseEdgeFlag << e; 269 | } 270 | } 271 | return baseTopo; 272 | } 273 | 274 | BARY_API uint32_t BARY_CALL baryBlockFormatDispC1GetMaxSubdivLevel(); 275 | 276 | // returns ~0 if invalid 277 | BARY_API uint32_t BARY_CALL baryBlockFormatDispC1GetSubdivLevel(BlockFormatDispC1 blockFormat); 278 | // returns ~0 if invalid 279 | BARY_API uint32_t BARY_CALL baryBlockFormatDispC1GetByteSize(BlockFormatDispC1 blockFormat); 280 | 281 | // returns 0 if invalid 282 | BARY_API uint32_t BARY_CALL baryBlockFormatDispC1GetBlockCount(BlockFormatDispC1 blockFormat, uint32_t baseSubdivLevel); 283 | 284 | BARY_API Result BARY_CALL baryBlockFormatDispC1GetBlockTriangles(BlockFormatDispC1 blockFormat, 285 | uint32_t baseSubdivLevel, 286 | uint32_t blockTrisCount, 287 | BlockTriangle* blockTris); 288 | 289 | BARY_API void BARY_CALL baryBlockTriangleSplitDispC1(const BlockTriangle* in, BlockTriangle* out, uint32_t outStride); 290 | 291 | 292 | BARY_API uint32_t BARY_CALL baryHistogramGetBlockCount(uint32_t entriesCount, const bary::HistogramEntry* entries, bary::Format fmt); 293 | BARY_API uint32_t BARY_CALL baryMeshHistogramGetBlockCount(uint32_t entriesCount, const bary::MeshHistogramEntry* entries, bary::Format fmt); 294 | 295 | ////////////////////////////////////////////////////////////////////////// 296 | // validation 297 | 298 | enum ValidationFlagBit : uint64_t 299 | { 300 | // If set, then deeper validation is performed on content arrays 301 | eValidationFlagArrayContents = 1ull, 302 | 303 | // If set, then accurate testing of Triangle::valuesByteOffset will be done. 304 | // Might want to skip if the compression blockformat is not known to the standard validation, 305 | // or if the compression blocks are used in a more complex way than simple uniform 306 | // splitting. 307 | eValidationFlagTriangleValueRange = 2ull, 308 | }; 309 | 310 | // all properties that constitute the final file should be provided 311 | // only standard properties can be validated here 312 | // errorPropertyType that caused an issue can be returned in pointer 313 | BARY_API Result BARY_CALL baryValidateStandardProperties(uint32_t propertyCount, 314 | const PropertyStorageInfo* propertyStorageInfos, 315 | uint64_t validationFlags, 316 | StandardPropertyType* errorPropertyType); 317 | 318 | 319 | ////////////////////////////////////////////////////////////////////////// 320 | // storage 321 | // 322 | // The typical ordering of operations would be: 323 | // 1. `baryValidateStandardProperties(...);` - heavy full validation on standard property types 324 | // 2. `fileSize = baryStorageComputeSize(...);` 325 | // 3. `baryStorageOutputAll(..., fileSize, filePointer);` 326 | // or 327 | // 3. `baryStorageOutputHeaderAndPropertyInfos(..., fileSize, filePointer)` 328 | // 4. iterate over properties: 329 | // `range = baryDataGetPropertyByteRange(fileSize, filePointer, property identifier);` 330 | // `dst = baryDataGetByteRangeDataT(fileSize, filePointer, *range);` 331 | // write property data to dst 332 | 333 | // computes size of header and prop infos 334 | BARY_API uint64_t BARY_CALL baryStorageComputePreambleSize(uint32_t propertyCount); 335 | 336 | // computes total outputSize required to store the provided properties along with all information 337 | // that makes a bary file (header + PropertyInfo(s)) 338 | BARY_API uint64_t BARY_CALL baryStorageComputeSize(uint32_t propertyCount, const PropertyStorageInfo* propertyStorageInfos); 339 | 340 | 341 | // all in one serialization to target outputData 342 | // outputSize must match baryStorageComputeSize 343 | BARY_API Result BARY_CALL baryStorageOutputAll(uint32_t propertyCount, 344 | const PropertyStorageInfo* propertyStorageInfos, 345 | uint64_t outputSize, 346 | void* outputData); 347 | 348 | // splits serialization, leaves writing per-property data into appropriate byte range inside outputData 349 | // to user. Use baryDataGetPropertyByteRange to get the range into outputData after this function was run 350 | // and then write the property data there. 351 | // 352 | // outputSize is expected to match baryStorageComputeSize 353 | // outputPreambleSize must match baryStorageComputePreambleSize 354 | BARY_API Result BARY_CALL baryStorageOutputPreamble(uint32_t propertyCount, 355 | const PropertyStorageInfo* propertyStorageInfos, 356 | uint64_t outputSize, 357 | uint64_t outputPreambleSize, 358 | void* outputPreambleData); 359 | 360 | // Callback to a function that saves `size` bytes, contained in `data`, 361 | // to the bytes starting at `offset` in the output (e.g. a file or in-memory buffer). 362 | // `offset` will be called in strictly ascending order without gaps, so 363 | // an implementation using `fwrite()` wouldn't need to call `setg()`. 364 | // `propertyStorageInfo != nullptr` if and only if a property is being written. 365 | typedef Result (*PFN_outputSaver)(uint32_t propertyIdx, 366 | const PropertyStorageInfo* propertyStorageInfo, 367 | uint64_t offset, 368 | uint64_t size, 369 | const void* data, 370 | bool isInfo, 371 | void* userData); 372 | 373 | // output via saver callback 374 | BARY_API Result BARY_CALL baryStorageOutputSaver(uint32_t propertyCount, 375 | const PropertyStorageInfo* propertyStorageInfos, 376 | uint64_t preambleSize, 377 | const void* preamble, 378 | PFN_outputSaver fnSaver, 379 | void* userData); 380 | 381 | ////////////////////////////////////////////////////////////////////////// 382 | // retrieval 383 | // 384 | // typical order of operations: 385 | // 1. `baryDataIsValid(fileSize, filePointer);` - checks the header and if all property infos are valid 386 | // (2. `baryDataHasMandatoryStandardProperties(fileSize, filePointer);` - checks if the mandatory standard properties exist in file with current uuids 387 | // 3. iterate properties and access their data 388 | // use `baryDataGetByteRangeDataT` and `baryDataGetByteRangeDataT` 389 | // or `baryDataGetPropertyData` or `baryDataGetPropertyDataT` 390 | // to make the appropriate property identifier: 391 | // for StandardPropertyTypes use `baryMakeStandardPropertyIdentifierT()` 392 | // anything else needs extra header/implementation of `baryMakePropertyIdentifierT()` or 393 | // other means of storage 394 | 395 | BARY_API Result BARY_CALL baryDataIsValid(uint64_t fileSize, const void* fileData); 396 | 397 | BARY_API Result BARY_CALL baryDataGetVersion(uint64_t fileSize, const void* fileData, uint32_t* pVersion); 398 | 399 | inline bool baryDataIsRangeValid(uint64_t fileSize, ByteRange range) 400 | { 401 | // in practice all byteOffsets must be >= aligned(sizeof(Header), 4) 402 | // however we don't test this given the header size could change as well 403 | return (range.byteOffset < fileSize) && (range.byteLength <= fileSize - range.byteOffset); 404 | } 405 | 406 | BARY_API Result BARY_CALL baryDataHasMandatoryStandardProperties(uint64_t fileSize, const void* fileData); 407 | 408 | BARY_API bool BARY_CALL baryDataHasAnySuperCompression(uint64_t fileSize, const void* fileData); 409 | 410 | // can return nullptr if identifier not found in file 411 | // returned pointer is within provided fileData memory 412 | BARY_API const PropertyInfo* BARY_CALL baryDataGetPropertyInfo(uint64_t fileSize, const void* fileData, PropertyIdentifier identifier); 413 | 414 | // can return nullptr if identifier not found in file 415 | // returned pointer is within provided fileData memory 416 | BARY_API const void* BARY_CALL baryDataGetPropertyData(uint64_t fileSize, const void* fileData, PropertyIdentifier identifier, uint64_t* pLength); 417 | 418 | // can return nullptr if no properties 419 | BARY_API const PropertyInfo* BARY_CALL baryDataGetAllPropertyInfos(uint64_t fileSize, const void* fileData, uint64_t* count); 420 | 421 | // returned pointer is within provided fileData memory 422 | template 423 | const T* baryDataGetByteRangeDataT(uint64_t fileSize, const void* fileData, ByteRange range) 424 | { 425 | if(baryDataIsRangeValid(fileSize, range)) 426 | { 427 | return reinterpret_cast(reinterpret_cast(fileData) + range.byteOffset); 428 | } 429 | 430 | return nullptr; 431 | } 432 | 433 | template 434 | T* baryDataGetByteRangeDataT(uint64_t fileSize, void* fileData, ByteRange range) 435 | { 436 | if(baryDataIsRangeValid(fileSize, range)) 437 | { 438 | return reinterpret_cast(reinterpret_cast(fileData) + range.byteOffset); 439 | } 440 | 441 | return nullptr; 442 | } 443 | 444 | // can return nullptr if identifier not found in file 445 | // returned pointer is within provided fileData memory 446 | template 447 | inline const T* baryDataGetPropertyDataT(uint64_t fileSize, const void* fileData, PropertyIdentifier identifier, uint64_t* pLength) 448 | { 449 | uint64_t length = 0; 450 | const T* result = reinterpret_cast(baryDataGetPropertyData(fileSize, fileData, identifier, &length)); 451 | // Always set pLength; length will be 0 if result is nullptr 452 | if(pLength) 453 | *pLength = length; 454 | // Make sure the property data really is long enough to contain a T 455 | if(length < sizeof(T)) 456 | return nullptr; 457 | return result; 458 | } 459 | 460 | 461 | } // namespace bary -------------------------------------------------------------------------------- /include/bary/bary_types.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #pragma once 19 | 20 | #include 21 | #include 22 | 23 | namespace bary 24 | { 25 | // bary 26 | // ==== 27 | // 28 | // bary is a container for data stored on a barycentric grid that 29 | // is the result of uniform subdivision. 30 | // There are three main properties stored in a bary file: 31 | // - Triangles 32 | // - Values 33 | // - Groups 34 | // 35 | // An input `Triangle` is subdivided evenly and as result creates 36 | // `2 ^ (sudvision level * 2)` output triangles. The bary file will store 37 | // either per-vertex or per-triangle data (`ValueFrequency`) for those outputs 38 | // on a spatial curve (`ValueOrder`) across the subdivided triangle. 39 | // Each `Triangle` stores the subdivision level as well as an offset where 40 | // the `Values` for this triangle start. 41 | // There is typically a 1:1 mapping between a 3d mesh triangle and the 42 | // bary file triangle, but it's also possible a custom mapping buffer is 43 | // used (a bit like a UV coordinate). 44 | // 45 | // The bary container can contain multiple independent `Groups` of triangle 46 | // and values pairings. This is useful to store barycentric data for multiple 47 | // 3d meshes in one file. 48 | // 49 | // Be aware, as barycentric data is relative to the winding of mesh triangles, there is a 50 | // strong coupling between the 3D mesh and its barycentric data. 51 | // For regular textures UV-coordinates are responsible for the coupling and allow 52 | // to work independent of topological changes, but for barycentric data that is not 53 | // the case. Barycentric data should be considered "for final deployment" foremost 54 | // and not necessarily as portable data container while assets are being changed. 55 | // 56 | 57 | ////////////////////////////////////////////// 58 | 59 | enum class Result : uint32_t 60 | { 61 | eSuccess = 0, 62 | eErrorUnknown, 63 | // provided file size is mismatching or too small 64 | eErrorFileSize, 65 | // ?Offset/?Length or ?First/?Count pair doesn't fit 66 | eErrorRange, 67 | // index doesn't fit 68 | eErrorIndex, 69 | // ?Offset or ?First has unexpected value 70 | // typically affects values with strict offset/first requirements 71 | eErrorOffset, 72 | // ?Size is unexpected 73 | eErrorSize, 74 | // ?Length/?Count is unexpected 75 | eErrorCount, 76 | // ?Alignment is unexpected 77 | eErrorAlignment, 78 | // version identifier format is invalid 79 | eErrorVersionFormat, 80 | // version does not match this header 81 | eErrorVersion, 82 | // unexpected format usage 83 | eErrorFormat, 84 | // unexpected block format usage 85 | eErrorBlockFormat, 86 | // the value of a provided parameter is invalid 87 | eErrorValue, 88 | // the value of a provided bit flag is invalid 89 | eErrorFlag, 90 | // the ordering of offsets (?Offset / ?First) is wrong 91 | eErrorOffsetOrder, 92 | // the ordering of an index is wrong 93 | eErrorIndexOrder, 94 | // the alignment of an offset is wrong 95 | eErrorOffsetAlignment, 96 | // a mandatory property is missing 97 | eErrorMissingProperty, 98 | // a property existed more than once 99 | eErrorDuplicateProperty, 100 | // a property cannot be used due to another property's state 101 | eErrorInvalidPropertyCombination, 102 | // a property cannot be used due to incompatibility (e.g. format mismatch) 103 | eErrorPropertyMismatch, 104 | // the supercompression state is unexpected 105 | eErrorSupercompression, 106 | 107 | // these are for external tools 108 | // as the core lib doesn't allocate memory, 109 | // nor do file operations 110 | // IO operation failed 111 | eErrorIO, 112 | // Allocation failed 113 | eErrorOutOfMemory, 114 | }; 115 | 116 | // defines the storage layout, which affects the ordering of values within a subdivided primitive 117 | enum class ValueLayout : uint32_t 118 | { 119 | eUndefined, 120 | 121 | // eTriangleUmajor 122 | // is a simple ordering of rows 123 | // parallel to the WV edge starting from W 124 | // towards U (aka u-major). 125 | // 126 | // Vertex ordering 127 | // ******************************* 128 | // * V * 129 | // * 4 * 130 | // * / \ * 131 | // * 3 __ 8 * 132 | // * / \ / \ * 133 | // * 2 __ 7 __ 11 * 134 | // * / \ / \ / \ * 135 | // * 1 __ 6 __ 10 __ 13 * 136 | // * / \ / \ / \ / \ * 137 | // * 0 __ 5 __ 9 __ 12 __ 14 * 138 | // * W U * 139 | // ******************************* 140 | // 141 | // Triangle ordering 142 | // ****************************** 143 | // * V * 144 | // * . * 145 | // * / 6\ * 146 | // * . __ . * 147 | // * / 4\5 /11\ * 148 | // * . __ . __ . * 149 | // * / 2\3 / 9\10/14\ * 150 | // * . __ . __ . __ . * 151 | // * / 0\1 / 7\8 /12\13/15\ * 152 | // * . __ . __ . __ . __ . * 153 | // * W U * 154 | // ****************************** 155 | // 156 | // values stored left to right, bottom to top 157 | // triangles stored in same ordering 158 | // 159 | 160 | eTriangleUmajor, 161 | 162 | // eTriangleBirdCurve is a 163 | // special hierarchical space filling curve 164 | // 165 | // each subdiv level adds the new vertices that are the result 166 | // of splitting triangles along their edges. 167 | // 168 | // Vertex ordering 169 | // ***************************************************************** 170 | // * V `` subdiv level 0 * 171 | // * 2`` ` subdiv level 1 (splits: 0,1,2) * 172 | // * / \ rest subdiv level 2 (splits: 0,5,3 * 173 | // * 12 __ 13 5,1,4 * 174 | // * / \ / \ 3,4,2) * 175 | // * 3`__ 14 4` * 176 | // * / \ / \ / \ * 177 | // * 6 __ 7 __ 9 __ 10 * 178 | // * / \ / \ / \ / \ * 179 | // * 0``__ 8 __ 5` __ 11 __ 1`` * 180 | // * W U * 181 | // ***************************************************************** 182 | // 183 | // example triangle order for subdiv level 2 184 | // 185 | // ****************************** 186 | // * Triangle ordering * 187 | // * V * 188 | // * . * 189 | // * /15\ * 190 | // * . __ . * 191 | // * /14\13/12\ * 192 | // * . __ . __ . * 193 | // * / 3\4 / 5\ 6/11\ * 194 | // * . __ . __ . __ . * 195 | // * / 0\1 / 2\7 / 8\ 9/10\ * 196 | // * . __ . __ . __ . __ . * 197 | // * W U * 198 | // ****************************** 199 | 200 | eTriangleBirdCurve, 201 | }; 202 | 203 | enum class ValueFrequency : uint32_t 204 | { 205 | eUndefined, 206 | ePerVertex, 207 | ePerTriangle, 208 | }; 209 | 210 | enum class ValueSemanticType : uint32_t 211 | { 212 | // any value format 213 | eGeneric, 214 | 215 | // For scalar displacement the following value formats are acceptable: 216 | // eR8_unorm 217 | // eDispC1_r11_unorm_block (is compressed) 218 | // eR11_unorm_pack16 219 | // eR11_unorm_packed_align32 220 | // eR16_unorm 221 | // eR32_sfloat 222 | eDisplacement, 223 | }; 224 | 225 | // values 226 | enum class Format : uint32_t 227 | { 228 | // enum values match VK_FORMAT 229 | 230 | eUndefined = 0, 231 | eR8_unorm = 9, 232 | eR8_snorm = 10, 233 | eR8_uint = 13, 234 | eR8_sint = 14, 235 | eRG8_unorm = 16, 236 | eRG8_snorm = 17, 237 | eRG8_uint = 20, 238 | eRG8_sint = 21, 239 | eRGB8_unorm = 23, 240 | eRGB8_snorm = 24, 241 | eRGB8_uint = 27, 242 | eRGB8_sint = 28, 243 | eRGBA8_unorm = 37, 244 | eRGBA8_snorm = 38, 245 | eRGBA8_uint = 41, 246 | eRGBA8_sint = 42, 247 | eR16_unorm = 70, 248 | eR16_snorm = 71, 249 | eR16_uint = 74, 250 | eR16_sint = 75, 251 | eR16_sfloat = 76, 252 | eRG16_unorm = 77, 253 | eRG16_snorm = 78, 254 | eRG16_uint = 81, 255 | eRG16_sint = 82, 256 | eRG16_sfloat = 83, 257 | eRGB16_unorm = 84, 258 | eRGB16_snorm = 85, 259 | eRGB16_uint = 88, 260 | eRGB16_sint = 89, 261 | eRGB16_sfloat = 90, 262 | eRGBA16_unorm = 91, 263 | eRGBA16_snorm = 92, 264 | eRGBA16_uint = 95, 265 | eRGBA16_sint = 96, 266 | eRGBA16_sfloat = 97, 267 | eR32_uint = 98, 268 | eR32_sint = 99, 269 | eR32_sfloat = 100, 270 | eRG32_uint = 101, 271 | eRG32_sint = 102, 272 | eRG32_sfloat = 103, 273 | eRGB32_uint = 104, 274 | eRGB32_sint = 105, 275 | eRGB32_sfloat = 106, 276 | eRGBA32_uint = 107, 277 | eRGBA32_sint = 108, 278 | eRGBA32_sfloat = 109, 279 | eR64_uint = 110, 280 | eR64_sint = 111, 281 | eR64_sfloat = 112, 282 | eRG64_uint = 113, 283 | eRG64_sint = 114, 284 | eRG64_sfloat = 115, 285 | eRGB64_uint = 116, 286 | eRGB64_sint = 117, 287 | eRGB64_sfloat = 118, 288 | eRGBA64_uint = 119, 289 | eRGBA64_sint = 120, 290 | eRGBA64_sfloat = 121, 291 | 292 | 293 | // opacity encoding (based on VK NV extension reservation 397) 294 | 295 | // block-compressed opacity format 296 | // for uncompressed 1 or 2 bit data stored in 8-bit 297 | // valueByteSize = 1 298 | eOpaC1_rx_uint_block = 1000396000, 299 | 300 | // displacement encoding (based on VK NV extension reservation 398) 301 | 302 | // block-compressed displacement format 303 | // for compressed data stored in blocks of 512- or 1024-bit 304 | // valueByteSize = 1 305 | // valueByteAlignment = 128 306 | // minmax as eR11_unorm_pack16 307 | eDispC1_r11_unorm_block = 1000397000, 308 | 309 | // for uncompressed data 1 x 11 bit stored in 16-bit 310 | eR11_unorm_pack16 = 1000397001, 311 | 312 | // variable packed format 313 | // for uncompressed data 1 x 11 bit densely packed sequence of 32bit values. 314 | // Each triangle starts at a 32-bit boundary. 315 | // valueByteSize = 1 316 | // minmax as eR11_unorm_pack16 317 | eR11_unorm_packed_align32 = 1000397002, 318 | }; 319 | 320 | enum class BlockFormatOpaC1 : uint16_t 321 | { 322 | eInvalid = 0, 323 | eR1_uint_x8 = 1, 324 | eR2_uint_x4 = 2, 325 | }; 326 | 327 | enum class BlockFormatDispC1 : uint16_t 328 | { 329 | eInvalid = 0, 330 | eR11_unorm_lvl3_pack512 = 1, 331 | eR11_unorm_lvl4_pack1024 = 2, 332 | eR11_unorm_lvl5_pack1024 = 3, 333 | }; 334 | 335 | struct ValueFloatVector 336 | { 337 | float r; 338 | float g; 339 | float b; 340 | float a; 341 | }; 342 | 343 | ////////////////////////////////////////////////////////////////////////// 344 | // Bary File Memory Layout 345 | // 346 | // - Preamble 347 | // - `Header` 348 | // - array of `PropertyInfo` (pointed to by `Header::propertyInfoRange`) 349 | // - data for all properties (pointed to by `PropertyInfo::range`) 350 | // - data might be interleaved with `PropertyInfo::supercompressionGlobalData` 351 | // after each property 352 | // 353 | // All byte alignments must be power of 2 and at least 4 bytes 354 | 355 | struct VersionIdentifier 356 | { 357 | char data[16]; 358 | }; 359 | 360 | // use baryGetCurrentVersionIdentifier() 361 | // '\xAB', 'B', 'A', 'R', 'Y', ' ', '0', '0', '1', '0', '0', '\xBB', '\r', '\n', '\x1A', '\n' 362 | 363 | struct ByteRange 364 | { 365 | // unless mentioned otherwise must be 4 byte aligned 366 | uint64_t byteOffset; 367 | uint64_t byteLength; 368 | }; 369 | 370 | struct Header 371 | { 372 | VersionIdentifier version; 373 | // size includes sizeof(header) and all subsequent properties 374 | uint64_t totalByteSize; 375 | // stores PropertyInfo[] 376 | // all PropertyInfo byte ranges must come after header and stay within 377 | // totalByteSize, their ranges must be non-overlapping and 378 | // ordered with ascending ByteRange::byteOffset 379 | ByteRange propertyInfoRange; 380 | }; 381 | 382 | struct PropertyIdentifier 383 | { 384 | uint32_t uuid4[4]; 385 | }; 386 | 387 | 388 | // Properties can be super-compressed. 389 | // At the time of writing this feature is not in use yet, but anticipated for future use. 390 | // If a property contains a leading preamble / "Info" struct, that struct will always 391 | // exist uncompressed (including padding), only the variable length payload will be 392 | // compressed. 393 | enum class SupercompressionScheme : uint32_t 394 | { 395 | eNone = 0, 396 | }; 397 | 398 | struct PropertyInfo 399 | { 400 | // UUID for a property, every new property or every time the 401 | // content definition of a property changes, its identifier must as well. 402 | PropertyIdentifier identifier; 403 | // byte range must be after the header, and within 404 | // byteSize of header. 405 | ByteRange range; 406 | 407 | SupercompressionScheme supercompressionScheme; 408 | // if `supercompressionScheme` is != SupercompressionScheme::eNone 409 | // then 'range.byteLength` is the supercompressed size and 410 | // this value provides the length after decompression. 411 | uint64_t uncompressedByteLength; 412 | // if exists, global data must be 8 byte aligned 413 | // and come after the above primary `range` for this 414 | // property 415 | ByteRange supercompressionGlobalData; 416 | }; 417 | 418 | ////////////////////////////////////////////////////////////////////////// 419 | // Utilities for saving and validation 420 | 421 | // utility structure not stored in bary file, 422 | // used for saving & validation 423 | struct PropertyStorageInfo 424 | { 425 | // which property is stored 426 | PropertyIdentifier identifier; 427 | 428 | // total size of the property will be the sum of dataSize, infoSize and infoPaddingSize 429 | 430 | // data of the property 431 | uint64_t dataSize = 0; 432 | const void* data = nullptr; 433 | 434 | // optional, for convenience the leading "info" struct of a property 435 | // can be provided separately 436 | // `data` pointer is then assumed to be the payload after the info struct 437 | // `dataSize` is then the size of the payload alone. 438 | uint64_t infoSize = 0; 439 | // if padding between `info` and `data` section is required. 440 | uint64_t infoPaddingSize = 0; 441 | const void* info = nullptr; 442 | 443 | SupercompressionScheme supercompressionScheme = SupercompressionScheme::eNone; 444 | uint64_t uncompressedSize = 0; 445 | uint64_t supercompressionGlobalDataSize = 0; 446 | const void* supercompressionGlobalData = nullptr; 447 | 448 | PropertyStorageInfo() { identifier.uuid4[0] = identifier.uuid4[1] = identifier.uuid4[2] = identifier.uuid4[3] = 0; } 449 | }; 450 | 451 | 452 | // Standard properties use the current uuids and definitions in this header. 453 | // 454 | // Custom or older properties can still be stored in a file. 455 | // However, then such properties are defined separately 456 | // (e.g. bary_deprecated.h) and are no longer a standard type 457 | // 458 | // utility enum not stored in bary file 459 | // used for saving & validation 460 | enum class StandardPropertyType : uint32_t 461 | { 462 | // non-standard properties can be stored as well 463 | // those are strictly identified by the UUID4 PropertyIdentifier 464 | eUnknown, 465 | 466 | // Mandatory properties: 467 | 468 | // stores ValueInfo + values 469 | eValues, 470 | // stores Group[] 471 | eGroups, 472 | // stores Triangle[] 473 | eTriangles, 474 | 475 | // Optional properties: 476 | // some can be considered mandatory depending on the usage 477 | // of a file. See `bary_displacement.h` for such an example. 478 | 479 | // stores TriangleMinMaxsInfo + data 480 | // These contain lower and upper bounds on the data per triangle; 481 | // including or computing them can improve performance of rasterization 482 | // implementations (e.g. by improving occlusion culling bounds). 483 | eTriangleMinMaxs, 484 | 485 | // stores TriangleUncompressedMip[] 486 | // info for uncompressed lower resolution mip-level for triangles 487 | // (can be sparse) 488 | // Including or computing these can improve performance of rasterization 489 | // implementations by simplifying decoding of distance data at low LOD. 490 | eTriangleUncompressedMips, 491 | // stores TriangleUncompressedMipDataInfo + data 492 | // uncompressed mip data referenced by TriangleUncompressedMip 493 | eUncompressedMips, 494 | // stores GroupUncompressedMip[] 495 | // must match Group[] count 496 | eGroupUncompressedMips, 497 | 498 | // stores HistogramEntry[] 499 | eHistogramEntries, 500 | // stores GroupHistogramRange[] per group 501 | eGroupHistograms, 502 | 503 | // Mesh properties: 504 | // may, or may not be stored within a bary file 505 | // Depending on the application and 3d mesh file 506 | // that this bary file is used with, these properties may be stored 507 | // in the 3d mesh file itself or referenced as being part of the bary file. 508 | // It is important to note that a strong coupling between 3d mesh 509 | // and its barycentric data exist. 510 | 511 | // stores MeshGroup[] per mesh group 512 | eMeshGroups, 513 | 514 | // stores MeshHistogramEntry[] 515 | eMeshHistogramEntries, 516 | // stores MeshGroupHistogramRange[] per mesh group 517 | // meshTriangleMappings are applied to account 518 | // for bary::Triangle being instanced from 519 | // multiple mesh triangles. 520 | eMeshGroupHistograms, 521 | 522 | // stores {MeshDirectionsInfo + data} 523 | // used for displacement, 3d mesh per-vertex 524 | eMeshDisplacementDirections, 525 | 526 | // stores MeshDirectionBoundsInfo + data 527 | // used for displacement, 3d mesh per-vertex 528 | eMeshDisplacementDirectionBounds, 529 | 530 | // stores MeshTriangleMappingsInfo + data 531 | // index to map a 3d mesh triangle to a bary file triangle 532 | eMeshTriangleMappings, 533 | 534 | // stores MeshTriangleFlagsInfo + data 535 | // special per mesh triangle flags: 536 | // - currently 1 bit per edge, if set means the neighboring triangle has 537 | // one subdivision level less. Edge order for the triangle (v0,v1,v2) 538 | // is (v0,v1) (v1,v2) (v2,v0) 539 | eMeshTriangleFlags, 540 | 541 | // stores MeshPositionsInfo + data 542 | // bary files typically don't store this, but useful for debugging 543 | // or asserting the 3d mesh matches expectations / triangle winding... 544 | eMeshPositions, 545 | 546 | // stores MeshTrianglesInfo + data 547 | // bary files typically don't store this, but useful for debugging 548 | // or asserting the 3d mesh matches expectations / triangle winding... 549 | eMeshTriangleIndices, 550 | }; 551 | 552 | template 553 | PropertyIdentifier baryMakeStandardPropertyIdentifierT(); 554 | 555 | template 556 | PropertyIdentifier baryMakePropertyIdentifierT(); 557 | 558 | #define BARY_MAKE_STANDARD_PROPERTY_IDENTIFIER(t, e, u0, u1, u2, u3) \ 559 | template <> \ 560 | inline PropertyIdentifier baryMakeStandardPropertyIdentifierT() \ 561 | { \ 562 | return {u0, u1, u2, u3}; \ 563 | } \ 564 | template <> \ 565 | inline PropertyIdentifier baryMakePropertyIdentifierT() \ 566 | { \ 567 | return {u0, u1, u2, u3}; \ 568 | } 569 | 570 | #define BARY_MAKE_PROPERTY_IDENTIFIER(t, u0, u1, u2, u3) \ 571 | template <> \ 572 | inline PropertyIdentifier baryMakePropertyIdentifierT() \ 573 | { \ 574 | return {u0, u1, u2, u3}; \ 575 | } 576 | 577 | /* 578 | lua code to generate numbers from uuid strings 579 | for uuid in test:gmatch("([%w%-]+)") do 580 | uuid = uuid:gsub("%-","") 581 | print("0x"..uuid:sub(1,8)..",0x"..uuid:sub(9,16)..",0x"..uuid:sub(17,24)..",0x"..uuid:sub(25,32)) 582 | end 583 | */ 584 | 585 | ////////////////////////////////////////////////////////////////////////// 586 | // Bary File Property Data 587 | 588 | struct ValuesInfo 589 | { 590 | Format valueFormat; 591 | // spatial layout of values across the subdivided triangle 592 | ValueLayout valueLayout; 593 | // per-vertex or per-triangle 594 | ValueFrequency valueFrequency; 595 | // how many values there are in total (or bytes for compressed / special packed formats) 596 | uint32_t valueCount; 597 | // compressed or special packed formats must use 598 | // valueByteSize 1 599 | uint32_t valueByteSize; 600 | // valueByteAlignment must be at least 4 bytes, higher alignment only 601 | // if it is power of two and either matching valueByteSize, or if special formats 602 | // demand for it. (e.g. eRG32_sfloat is 8 byte aligned, but eRGB32_sfloat 4 byte) 603 | uint32_t valueByteAlignment; 604 | // followed by padding (if required) then values data 605 | }; 606 | // stores ValueInfo + data 607 | // property size = sizeof(ValueInfo) + (padding) + valueCount * valueByteSize 608 | BARY_MAKE_STANDARD_PROPERTY_IDENTIFIER(ValuesInfo, StandardPropertyType::eValues, 0xb44daa04, 0xc9e044d5, 0x9a944de0, 0xcfd8fe35) 609 | 610 | struct Group 611 | { 612 | // first/count must be linear ascending and non-overlapping 613 | 614 | uint32_t triangleFirst; 615 | uint32_t triangleCount; 616 | uint32_t valueFirst; 617 | uint32_t valueCount; 618 | 619 | uint32_t minSubdivLevel; 620 | uint32_t maxSubdivLevel; 621 | 622 | // for UNORM,SNORM,FLOAT values these 623 | // represent the final value range 624 | // (value * scale) + bias 625 | ValueFloatVector floatBias; 626 | ValueFloatVector floatScale; 627 | }; 628 | // stores Group[] 629 | BARY_MAKE_STANDARD_PROPERTY_IDENTIFIER(Group, StandardPropertyType::eGroups, 0x39ee40d0, 0x9dc44517, 0x8e5ab15d, 0xb09c74bc) 630 | 631 | struct Triangle 632 | { 633 | // valuesOffset must be ascending from t to t+1 634 | // and are relative to the group that this triangle belongs to 635 | // for uncompressed: serves as indexOffset (valueFormat agnostic) 636 | // for compressed / special packed: serves as byteOffset (given valueByteSize is 1) 637 | uint32_t valuesOffset; 638 | uint16_t subdivLevel; 639 | union 640 | { 641 | uint16_t blockFormat; 642 | BlockFormatDispC1 blockFormatDispC1; 643 | BlockFormatOpaC1 blockFormatOpaC1; 644 | }; 645 | }; 646 | // stores Triangle[] 647 | BARY_MAKE_STANDARD_PROPERTY_IDENTIFIER(Triangle, StandardPropertyType::eTriangles, 0x00458e68, 0xee59426c, 0xb3bf1b7f, 0x749deb8e) 648 | 649 | struct HistogramEntry 650 | { 651 | // The histogram provides detailed usages wihtin compressed files 652 | // to other processing steps and avoids iterating all triangles 653 | // manually. 654 | // 655 | // Each entry stores how many bary triangles are used 656 | // with a unique pairing of block format 657 | // and subdivision level. 658 | uint32_t count; 659 | uint32_t subdivLevel; 660 | union 661 | { 662 | // intentional 32-bit usage here 663 | uint32_t blockFormat; 664 | BlockFormatDispC1 blockFormatDispC1; 665 | BlockFormatOpaC1 blockFormatOpaC1; 666 | }; 667 | }; 668 | // stores HistogramEntry[] 669 | BARY_MAKE_STANDARD_PROPERTY_IDENTIFIER(HistogramEntry, StandardPropertyType::eHistogramEntries, 0x6e756bc1, 0x839a438a, 0xad4a745e, 0x556c3851) 670 | 671 | struct GroupHistogramRange 672 | { 673 | // into StandardPropertyType::eHistogramEntries 674 | // which histogram entries are valid for this group 675 | uint32_t entryFirst; 676 | uint32_t entryCount; 677 | }; 678 | 679 | // stores HistogramRange[] 680 | // 681 | BARY_MAKE_STANDARD_PROPERTY_IDENTIFIER(GroupHistogramRange, StandardPropertyType::eGroupHistograms, 0x400d972f, 0x76dd4337, 0x9900c6e9, 0xc9c46ffe) 682 | 683 | struct MeshGroup 684 | { 685 | uint32_t triangleFirst; 686 | uint32_t triangleCount; 687 | uint32_t vertexFirst; 688 | uint32_t vertexCount; 689 | }; 690 | 691 | BARY_MAKE_STANDARD_PROPERTY_IDENTIFIER(MeshGroup, StandardPropertyType::eMeshGroups, 0x94beebda, 0x7ec347f0, 0xa5f1de0e, 0xcfbb851b) 692 | 693 | struct MeshHistogramEntry 694 | { 695 | // guaranteed to match HistogramEntry 696 | 697 | // The histogram provides detailed usages wihtin compressed files 698 | // to other processing steps and avoids iterating all triangles 699 | // manually. 700 | // 701 | // Each entry stores how many mesh triangles are used 702 | // with a unique pairing of block format 703 | // and subdivision level. 704 | uint32_t count; 705 | uint32_t subdivLevel; 706 | union 707 | { 708 | // intentional 32-bit usage here 709 | uint32_t blockFormat; 710 | BlockFormatDispC1 blockFormatDispC1; 711 | BlockFormatOpaC1 blockFormatOpaC1; 712 | }; 713 | }; 714 | // stores MeshHistogramEntry[] 715 | BARY_MAKE_STANDARD_PROPERTY_IDENTIFIER(MeshHistogramEntry, StandardPropertyType::eMeshHistogramEntries, 0x68ce84e0, 0x7f4448f3, 0xbf58ba6e, 0x08d8cfd0) 716 | 717 | struct MeshGroupHistogramRange 718 | { 719 | // into StandardPropertyType::eMeshHistogramEntries 720 | // which histogram entries are valid for this mesh group 721 | // mesh groups only need to exist if TriangleMappings exist 722 | // so that multiple mesh triangles may map to the same bary triangle. 723 | uint32_t entryFirst; 724 | uint32_t entryCount; 725 | }; 726 | 727 | BARY_MAKE_STANDARD_PROPERTY_IDENTIFIER(MeshGroupHistogramRange, StandardPropertyType::eMeshGroupHistograms, 0x02a37898, 0x74564056, 0x93b1bed7, 0x7f05386e) 728 | 729 | struct MeshDisplacementDirectionsInfo 730 | { 731 | // eRGB32_sfloat, eRGBA32_sfloat, eRGB16_sfloat or eRGBA16_sfloat 732 | // per-vertex displacement directions (linearly interpolated, without normalization) 733 | // if omitted the mesh vertex normals are to be used 734 | Format elementFormat; 735 | uint32_t elementCount; 736 | uint32_t elementByteSize; 737 | uint32_t elementByteAlignment; // note: use 4 bytes for eRGB16_sfloat 738 | // followed by padding (if required) then values data 739 | }; 740 | 741 | // stores MeshDirectionsInfo + data 742 | // property size = sizeof(MeshDirectionsInfo) + count * elementByteSize 743 | BARY_MAKE_STANDARD_PROPERTY_IDENTIFIER(MeshDisplacementDirectionsInfo, 744 | StandardPropertyType::eMeshDisplacementDirections, 745 | 0xf262d687, 746 | 0xb9284aeb, 747 | 0xa706803c, 748 | 0xcbedae52) 749 | 750 | struct MeshDisplacementDirectionBoundsInfo 751 | { 752 | // eRG32_sfloat or eRG16_sfloat 753 | // per-vertex {bias,scale} 754 | // to maximize the quantization of displacement values, a per-vertex 755 | // {bias,scale} allows to define the shell for the unsigned normalized displacements. 756 | // 757 | // displaced_position = interpolated(vertex_position + vertex_displacement_direction * bounds_bias) + 758 | // interpolated(vertex_displacement_direction * bounds_scale) * displacement_value; 759 | // 760 | // `interpolated` stands for the linear barycentric interpolation of those resulting vertex values of the triangle 761 | // 762 | // If direction bounds are used, Group::floatBias must be 0 and Group::floatScale 1.0 763 | 764 | Format elementFormat; 765 | uint32_t elementCount; 766 | uint32_t elementByteSize; 767 | uint32_t elementByteAlignment; 768 | // followed by padding (if required) then values data 769 | }; 770 | // stores MeshDirectionBoundsInfo + data 771 | // property size = sizeof(MeshDirectionBoundsInfo) + count * elementByteSize 772 | BARY_MAKE_STANDARD_PROPERTY_IDENTIFIER(MeshDisplacementDirectionBoundsInfo, 773 | StandardPropertyType::eMeshDisplacementDirectionBounds, 774 | 0x25bf3c65, 775 | 0x29234ae1, 776 | 0x95efe43c, 777 | 0xeb87066c) 778 | 779 | struct MeshTriangleMappingsInfo 780 | { 781 | // eR32_uint or eR16_uint 782 | Format elementFormat; 783 | uint32_t elementCount; 784 | uint32_t elementByteSize; 785 | uint32_t elementByteAlignment; 786 | // followed by padding (if required) then values data 787 | }; 788 | // stores MeshTriangleMappingsInfo + data 789 | // property size = sizeof(MeshTriangleMappingsInfo) + count * elementByteSize 790 | BARY_MAKE_STANDARD_PROPERTY_IDENTIFIER(MeshTriangleMappingsInfo, StandardPropertyType::eMeshTriangleMappings, 0x9cdc3ad0, 0x92bb4a8a, 0xb4658759, 0x42d41d54) 791 | 792 | struct MeshTriangleFlagsInfo 793 | { 794 | // eR8_uint 795 | // special per mesh triangle flags: 796 | // - currently 1 bit per edge, if set means the neighboring triangle of that edge 797 | // has one subdivision level less. 798 | // Edge order for the triangle (v0,v1,v2) is (v0,v1) (v1,v2) (v2,v0) 799 | Format elementFormat; 800 | uint32_t elementCount; 801 | uint32_t elementByteSize; 802 | uint32_t elementByteAlignment; 803 | // followed by padding (if required) then values data 804 | }; 805 | // stores MeshTriangleFlagsInfo + data 806 | // property size = sizeof(MeshTriangleFlagsInfo) + count * elementByteSize 807 | BARY_MAKE_STANDARD_PROPERTY_IDENTIFIER(MeshTriangleFlagsInfo, StandardPropertyType::eMeshTriangleFlags, 0x90f9eed3, 0x4ec34974, 0x970c755c, 0xaf5b53a3) 808 | 809 | struct MeshPositionsInfo 810 | { 811 | // eRGB32_sfloat, eRGBA32_sfloat, eRGBA16_sfloat or eRGB16_sfloat (alpha ignored) 812 | Format elementFormat; 813 | uint32_t elementCount; 814 | uint32_t elementByteSize; 815 | uint32_t elementByteAlignment; // note: use 4 bytes for eRGB16_sfloat 816 | // followed by padding (if required) then values data 817 | }; 818 | // stores MeshPositionsInfo + data 819 | // property size = sizeof(MeshPositionsInfo) + count * elementByteSize 820 | BARY_MAKE_STANDARD_PROPERTY_IDENTIFIER(MeshPositionsInfo, StandardPropertyType::eMeshPositions, 0xac071cfe, 0xc01d430d, 0x936ff822, 0xa2d6b48e) 821 | 822 | struct MeshTriangleIndicesInfo 823 | { 824 | // eR32_uint or eR16_uint 825 | // 3 indices per triangle 826 | Format elementFormat; 827 | uint32_t elementCount; 828 | uint32_t elementByteSize; 829 | uint32_t elementByteAlignment; 830 | // followed by padding (if required) then values data 831 | }; 832 | // stores MeshTriangleIndicesInfo + data 833 | // property size = sizeof(MeshTriangleIndicesInfo) + count * elementByteSize 834 | BARY_MAKE_STANDARD_PROPERTY_IDENTIFIER(MeshTriangleIndicesInfo, StandardPropertyType::eMeshTriangleIndices, 0x48f106db, 0x1daf410f, 0x8cf1c35c, 0x69559309) 835 | 836 | struct TriangleMinMaxsInfo 837 | { 838 | // {min, max} value pairs per triangle 839 | // format must always be uncompressed 840 | Format elementFormat; 841 | // count must be 2 x triangle count 842 | uint32_t elementCount; 843 | uint32_t elementByteSize; 844 | uint32_t elementByteAlignment; 845 | // followed by padding (if required) then values data 846 | }; 847 | // stores TriangleMinMaxsInfo + data 848 | // property size = sizeof(TriangleMinMaxsInfo) + count * elementByteSize 849 | BARY_MAKE_STANDARD_PROPERTY_IDENTIFIER(TriangleMinMaxsInfo, StandardPropertyType::eTriangleMinMaxs, 0x23010706, 0x56744eb7, 0x8c0d6ced, 0x5138d2f9) 850 | 851 | struct TriangleUncompressedMip 852 | { 853 | // The element offset of this triangle's first value in the 854 | // UncompressedMipsInfo values array, relative to the start of the group. 855 | // This is equivalent to a byte offset of elementByteSize * mipOffset bytes. 856 | // Can be ~0 if this triangle doesn't need/have a special mip block; 857 | // otherwise, must be ascending from triangle t to t+1. 858 | uint32_t mipOffset; 859 | uint32_t subdivLevel; 860 | }; 861 | 862 | // stores TriangleUncompressedMip[] 863 | // count must match Triangle[] 864 | BARY_MAKE_STANDARD_PROPERTY_IDENTIFIER(TriangleUncompressedMip, StandardPropertyType::eTriangleUncompressedMips, 0xd8b5df1c, 0xcc2b41ef, 0x84fbb4df, 0xbff10a14) 865 | 866 | struct GroupUncompressedMip 867 | { 868 | // The element offset of this group's first value in the 869 | // UncompressedMipsInfo values array. This is equivalent to a byte offset 870 | // of elementByteSize * mipFirst bytes. 871 | uint32_t mipFirst; 872 | // The number of UncompressedMipsInfo values in this group. This group 873 | // spans elements mipFirst to (but not including) mipFirst + mipCount. 874 | uint32_t mipCount; 875 | }; 876 | 877 | // stores GroupUncompressedMip[] 878 | // count must match Group[] 879 | BARY_MAKE_STANDARD_PROPERTY_IDENTIFIER(GroupUncompressedMip, StandardPropertyType::eGroupUncompressedMips, 0x57094946, 0xc47b4603, 0x8cb8b6e8, 0x6221cd27) 880 | 881 | struct UncompressedMipsInfo 882 | { 883 | // if valueFormat == eDispC1_r11_unorm_block then format must be eR11_unorm_packed_align32 884 | Format elementFormat; 885 | uint32_t elementCount; 886 | uint32_t elementByteSize; 887 | uint32_t elementByteAlignment; 888 | // followed by padding (if required) then values data 889 | }; 890 | 891 | // stores TriangleUncompressedMipDataInfo + data 892 | // property size = sizeof(TriangleUncompressedMipDataInfo) + count * elementByteSize 893 | BARY_MAKE_STANDARD_PROPERTY_IDENTIFIER(UncompressedMipsInfo, StandardPropertyType::eUncompressedMips, 0x585a66e6, 0xcb334423, 0xbe821357, 0xc471f552) 894 | 895 | #undef BARY_MAKE_STANDARD_PROPERTY_IDENTIFIER 896 | 897 | ////////////////////////////////////////////// 898 | // 899 | // Following types are used in utility functions 900 | // and not stored in files itself. 901 | // 902 | ////////////////////////////////////////////// 903 | 904 | struct BaryUV_uint16 905 | { 906 | uint16_t u; 907 | uint16_t v; 908 | }; 909 | 910 | // BlockFormat compression splits a base triangle (bary::Triangle) 911 | // into block triangles. 912 | // Each of these occupies a region of the original base triangle 913 | // values. Each block format can cover a different number of 914 | // values depending on its implicit subdivision level. 915 | // BlockTriangle provides the details about the region and 916 | // orientation. 917 | 918 | struct BlockTriangle 919 | { 920 | // three UV coordinates of this block triangle 921 | // relative to original base triangle (bary::Triangle) 922 | BaryUV_uint16 vertices[3]; 923 | // flipped winding 0/1 924 | uint8_t flipped; 925 | // u and v sign relative to first vertex 926 | // bit 0: set if verticesUV[1].u > verticesUV[0].u 927 | // bit 1: set if verticesUV[2].v > verticesUV[0].v 928 | uint8_t signBits; 929 | // 3 x 2 bits that specify which local edge (0,1,2) maps to what 930 | // base edge (0,1,2) The value 3 means the local edge does not lie 931 | // on any base edge 932 | uint8_t baseEdgeIndices; 933 | uint8_t _reserved; 934 | // where this block's data starts relative to 935 | // original base triangle valuesOffset (which is in bytes for compressed data) 936 | uint32_t blockByteOffset; 937 | }; 938 | 939 | ////////////////////////////////////////////// 940 | 941 | // utility views on standard property bary content 942 | 943 | // BasicView is for properties that are most typical 944 | // stored in bary files 945 | struct BasicView 946 | { 947 | // mandatory for all 948 | const Group* groups = nullptr; 949 | uint32_t groupsCount = 0; 950 | const ValuesInfo* valuesInfo = nullptr; 951 | const uint8_t* values = nullptr; 952 | const Triangle* triangles = nullptr; 953 | uint32_t trianglesCount = 0; 954 | // mandatory for compressed 955 | const HistogramEntry* histogramEntries = nullptr; 956 | uint32_t histogramEntriesCount = 0; 957 | const GroupHistogramRange* groupHistogramRanges = nullptr; 958 | uint32_t groupHistogramRangesCount = 0; 959 | // mandatory for displacement otherwise optional 960 | const TriangleMinMaxsInfo* triangleMinMaxsInfo = nullptr; 961 | const uint8_t* triangleMinMaxs = nullptr; 962 | }; 963 | 964 | // MeshProps is for properties that may be stored in the 3d model file 965 | // rather than in the bary file 966 | struct MeshView 967 | { 968 | // optional mesh properties 969 | const MeshGroup* meshGroups = nullptr; 970 | uint32_t meshGroupsCount = 0; 971 | 972 | const MeshHistogramEntry* meshHistogramEntries = nullptr; 973 | uint32_t meshHistogramEntriesCount = 0; 974 | const MeshGroupHistogramRange* meshGroupHistogramRanges = nullptr; 975 | uint32_t meshGroupHistogramRangesCount = 0; 976 | 977 | const MeshDisplacementDirectionsInfo* meshDisplacementDirectionsInfo = nullptr; 978 | const uint8_t* meshDisplacementDirections = nullptr; 979 | const MeshDisplacementDirectionBoundsInfo* meshDisplacementDirectionBoundsInfo = nullptr; 980 | const uint8_t* meshDisplacementDirectionBounds = nullptr; 981 | 982 | const MeshTriangleMappingsInfo* meshTriangleMappingsInfo = nullptr; 983 | const uint8_t* meshTriangleMappings = nullptr; 984 | const MeshTriangleFlagsInfo* meshTriangleFlagsInfo = nullptr; 985 | const uint8_t* meshTriangleFlags = nullptr; 986 | 987 | // uncommon, meant for debugging 988 | const MeshPositionsInfo* meshPositionsInfo = nullptr; 989 | const uint8_t* meshPositions = nullptr; 990 | const MeshTriangleIndicesInfo* meshTriangleIndicesInfo = nullptr; 991 | const uint8_t* meshTriangleIndices = nullptr; 992 | }; 993 | 994 | // MiscPropsView is for properties not part of typical files 995 | struct MiscView 996 | { 997 | // optional mip properties 998 | const GroupUncompressedMip* groupUncompressedMips = nullptr; 999 | uint32_t groupUncompressedMipsCount = 0; 1000 | const TriangleUncompressedMip* triangleUncompressedMips = nullptr; 1001 | uint32_t triangleUncompressedMipsCount = 0; 1002 | const UncompressedMipsInfo* uncompressedMipsInfo = nullptr; 1003 | const uint8_t* uncompressedMips = nullptr; 1004 | }; 1005 | 1006 | struct ContentView 1007 | { 1008 | BasicView basic; 1009 | MeshView mesh; 1010 | MiscView misc; 1011 | }; 1012 | 1013 | } // namespace bary -------------------------------------------------------------------------------- /include/baryutils/baryutils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | #pragma once 18 | 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | // baryutils is designed to not depend on micromesh sdk 27 | // so that it can be easily used in apps that just want to load/save or 28 | // render existing data. 29 | 30 | namespace baryutils 31 | { 32 | ////////////////////////////////////////////////////////////////////////// 33 | 34 | inline uint32_t baryDisplacementFormatGetNumBits(bary::Format fmt) 35 | { 36 | switch(fmt) 37 | { 38 | case bary::Format::eR8_unorm: 39 | return 8; 40 | case bary::Format::eR16_unorm: 41 | case bary::Format::eR11_unorm_pack16: 42 | return 16; 43 | case bary::Format::eR32_sfloat: 44 | return 32; 45 | default: 46 | return 0; 47 | } 48 | } 49 | 50 | ////////////////////////////////////////////////////////////////////////// 51 | 52 | struct BaryBasicInfo 53 | { 54 | bary::ValuesInfo valuesInfo; 55 | bary::TriangleMinMaxsInfo triangleMinMaxsInfo; 56 | }; 57 | 58 | struct BaryMeshInfo 59 | { 60 | bary::MeshDisplacementDirectionsInfo meshDisplacementDirectionsInfo; 61 | bary::MeshDisplacementDirectionBoundsInfo meshDisplacementDirectionBoundsInfo; 62 | bary::MeshPositionsInfo meshPositionsInfo; 63 | bary::MeshTriangleMappingsInfo meshTriangleMappingsInfo; 64 | bary::MeshTriangleFlagsInfo meshTriangleFlagsInfo; 65 | bary::MeshTriangleIndicesInfo meshTriangleIndicesInfo; 66 | }; 67 | 68 | struct BaryMiscInfo 69 | { 70 | bary::UncompressedMipsInfo uncompressedMipsInfo; 71 | }; 72 | 73 | struct BaryContentInfo 74 | { 75 | BaryBasicInfo basic; 76 | BaryMeshInfo mesh; 77 | BaryMiscInfo misc; 78 | }; 79 | 80 | ////////////////////////////////////////////////////////////////////////// 81 | struct BaryFileOpenOptions; 82 | 83 | struct BaryBasicData 84 | { 85 | uint32_t minSubdivLevel = 0; 86 | uint32_t maxSubdivLevel = 0; 87 | 88 | // mandatory for all 89 | std::vector groups; 90 | bary::ValuesInfo valuesInfo{}; 91 | std::vector values; 92 | std::vector triangles; 93 | 94 | // mandatory for compressed 95 | std::vector histogramEntries; 96 | std::vector groupHistogramRanges; 97 | 98 | // optional, but can improve displacement rasterization performance 99 | bary::TriangleMinMaxsInfo triangleMinMaxsInfo{}; 100 | std::vector triangleMinMaxs; 101 | 102 | BaryBasicData() {} 103 | BaryBasicData(const bary::BasicView& basic) { setData(basic); } 104 | 105 | void setData(const bary::BasicView& basic); 106 | bary::BasicView getView() const; 107 | void updateMinMaxSubdivLevels(); 108 | 109 | template 110 | T* getValues() 111 | { 112 | return reinterpret_cast(values.data()); 113 | } 114 | 115 | template 116 | const T* getValues() const 117 | { 118 | return reinterpret_cast(values.data()); 119 | } 120 | 121 | bary::Result save(const char* filename, 122 | const bary::MeshView* mesh = nullptr, 123 | const bary::MiscView* misc = nullptr, 124 | bary::StandardPropertyType* pErrorProp = nullptr) const; 125 | bary::Result save(const std::string& filename, 126 | const bary::MeshView* mesh = nullptr, 127 | const bary::MiscView* misc = nullptr, 128 | bary::StandardPropertyType* pErrorProp = nullptr) const 129 | { 130 | return save(filename.c_str(), mesh, misc, pErrorProp); 131 | } 132 | 133 | bary::Result load(size_t fileSize, 134 | const void* fileData, 135 | bary::ValueSemanticType vtype = bary::ValueSemanticType::eGeneric, 136 | bary::StandardPropertyType* pErrorProp = nullptr); 137 | bary::Result load(const char* filename, 138 | bary::ValueSemanticType vtype = bary::ValueSemanticType::eGeneric, 139 | const BaryFileOpenOptions* fileOptions = nullptr, 140 | bary::StandardPropertyType* pErrorProp = nullptr); 141 | bary::Result load(const std::string& filename, 142 | bary::ValueSemanticType vtype = bary::ValueSemanticType::eGeneric, 143 | const BaryFileOpenOptions* fileOptions = nullptr, 144 | bary::StandardPropertyType* pErrorProp = nullptr) 145 | { 146 | return load(filename.c_str(), vtype, fileOptions, pErrorProp); 147 | } 148 | }; 149 | 150 | struct BaryMeshData 151 | { 152 | BaryMeshData() {} 153 | BaryMeshData(const bary::MeshView& view) { setData(view); } 154 | 155 | // optional mesh properties 156 | // (these may be stored inside a 3d mesh file format, or not exist at all) 157 | 158 | std::vector meshGroups; 159 | 160 | std::vector meshHistogramEntries; 161 | std::vector meshGroupHistogramRanges; 162 | 163 | bary::MeshDisplacementDirectionsInfo meshDisplacementDirectionsInfo{}; 164 | std::vector meshDisplacementDirections; 165 | bary::MeshDisplacementDirectionBoundsInfo meshDisplacementDirectionBoundsInfo{}; 166 | std::vector meshDisplacementDirectionBounds; 167 | 168 | bary::MeshTriangleMappingsInfo meshTriangleMappingsInfo{}; 169 | std::vector meshTriangleMappings; 170 | bary::MeshTriangleFlagsInfo meshTriangleFlagsInfo{}; 171 | std::vector meshTriangleFlags; 172 | 173 | // uncommon, meant for debugging 174 | bary::MeshPositionsInfo meshPositionsInfo{}; 175 | std::vector meshPositions; 176 | bary::MeshTriangleIndicesInfo meshTriangleIndicesInfo{}; 177 | std::vector meshTriangleIndices; 178 | 179 | void setData(const bary::MeshView& view); 180 | bary::MeshView getView() const; 181 | }; 182 | 183 | struct BaryMiscData 184 | { 185 | BaryMiscData() {} 186 | BaryMiscData(const bary::MiscView& view) { setData(view); } 187 | 188 | // optional 189 | std::vector groupUncompressedMips; 190 | std::vector triangleUncompressedMips; 191 | bary::UncompressedMipsInfo uncompressedMipsInfo{}; 192 | std::vector uncompressedMips; 193 | 194 | void setData(const bary::MiscView& view); 195 | bary::MiscView getView() const; 196 | }; 197 | 198 | struct BaryContentData 199 | { 200 | BaryContentData() {} 201 | BaryContentData(const bary::ContentView& view) { setData(view); } 202 | 203 | BaryBasicData basic; 204 | BaryMeshData mesh; 205 | BaryMiscData misc; 206 | 207 | void setData(const bary::ContentView& view) 208 | { 209 | basic.setData(view.basic); 210 | mesh.setData(view.mesh); 211 | misc.setData(view.misc); 212 | } 213 | 214 | bary::ContentView getView() const 215 | { 216 | bary::ContentView view; 217 | view.basic = basic.getView(); 218 | view.mesh = mesh.getView(); 219 | view.misc = misc.getView(); 220 | return view; 221 | } 222 | 223 | bary::Result save(const char* filename, bary::StandardPropertyType* pErrorProp = nullptr) const; 224 | bary::Result save(const std::string& filename, bary::StandardPropertyType* pErrorProp = nullptr) const 225 | { 226 | return save(filename.c_str(), pErrorProp); 227 | } 228 | 229 | bary::Result load(size_t fileSize, 230 | const void* fileData, 231 | bary::ValueSemanticType vtype = bary::ValueSemanticType::eGeneric, 232 | bary::StandardPropertyType* pErrorProp = nullptr); 233 | bary::Result load(const char* filename, 234 | bary::ValueSemanticType vtype = bary::ValueSemanticType::eGeneric, 235 | const BaryFileOpenOptions* fileOptions = nullptr, 236 | bary::StandardPropertyType* pErrorProp = nullptr); 237 | bary::Result load(const std::string& filename, 238 | bary::ValueSemanticType vtype = bary::ValueSemanticType::eGeneric, 239 | const BaryFileOpenOptions* fileOptions = nullptr, 240 | bary::StandardPropertyType* pErrorProp = nullptr) 241 | { 242 | return load(filename.c_str(), vtype, fileOptions, pErrorProp); 243 | } 244 | }; 245 | 246 | ////////////////////////////////////////////////////////////////////////// 247 | 248 | struct BaryStats 249 | { 250 | static uint32_t getHistoBin(uint32_t count); 251 | static const uint32_t MAX_HISTO_BINS = 32; 252 | 253 | uint32_t minSubdivLevel = ~0u; 254 | uint32_t maxSubdivLevel = 0; 255 | size_t dataByteSize = 0; 256 | bary::ValueLayout valueOrder = bary::ValueLayout::eUndefined; 257 | bary::Format valueFormat = bary::Format::eUndefined; 258 | 259 | uint32_t microTriangles = 0; 260 | uint32_t microVertices = 0; 261 | uint32_t mapTriangles = 0; 262 | uint32_t blocks = 0; 263 | // how many blocks of a certain format exist 264 | uint32_t blocksPerFormat[uint32_t(bary::BlockFormatDispC1::eR11_unorm_lvl5_pack1024) + 1] = {0}; 265 | // histogram over how many blocks triangles have 266 | // histogram in power-of-2 bins 267 | uint32_t blocksPerTriangleHisto[MAX_HISTO_BINS] = {0}; 268 | 269 | BaryStats() {} 270 | BaryStats(const bary::BasicView& basic) { append(basic); } 271 | 272 | // return true on inconsistent valueOrder 273 | bool append(const bary::BasicView& basic); 274 | }; 275 | 276 | ////////////////////////////////////////////////////////////////////////// 277 | 278 | struct BaryMeshViewWithInfo : bary::MeshView 279 | { 280 | BaryMeshInfo rw; 281 | 282 | void setupPointers() 283 | { 284 | meshDisplacementDirectionsInfo = &rw.meshDisplacementDirectionsInfo; 285 | meshDisplacementDirectionBoundsInfo = &rw.meshDisplacementDirectionBoundsInfo; 286 | meshPositionsInfo = &rw.meshPositionsInfo; 287 | meshTriangleFlagsInfo = &rw.meshTriangleFlagsInfo; 288 | meshTriangleMappingsInfo = &rw.meshTriangleMappingsInfo; 289 | meshTriangleIndicesInfo = &rw.meshTriangleIndicesInfo; 290 | } 291 | 292 | BaryMeshViewWithInfo() { setupPointers(); } 293 | BaryMeshViewWithInfo(const BaryMeshViewWithInfo& other) { *this = other; } 294 | BaryMeshViewWithInfo& operator=(const BaryMeshViewWithInfo& other) 295 | { 296 | // Avoid producing a warning about calling memcpy on a type without 297 | // trivial copy-assignment: in this case, we want to copy the bytes 298 | // of the class verbatim (for which the use of memcpy here is valid), 299 | // and then set up the new pointers. 300 | #if defined(__GNUC__) && !defined(__clang__) 301 | #pragma GCC diagnostic push 302 | #pragma GCC diagnostic ignored "-Wclass-memaccess" 303 | #endif 304 | memcpy(this, &other, sizeof(BaryMeshViewWithInfo)); 305 | #if defined(__GNUC__) && !defined(__clang__) 306 | #pragma GCC diagnostic pop 307 | #endif 308 | setupPointers(); 309 | return *this; 310 | } 311 | }; 312 | 313 | ////////////////////////////////////////////////////////////////////////// 314 | 315 | class BarySaver 316 | { 317 | public: 318 | // pointers within `content` must remain valid until saving completed 319 | bary::Result initContent(const bary::ContentView* content, bary::StandardPropertyType* pErrorProp = nullptr); 320 | 321 | // pointers within views must remain valid until saving completed 322 | bary::Result initContent(const bary::BasicView* basic, 323 | const bary::MeshView* mesh = nullptr, 324 | const bary::MiscView* misc = nullptr, 325 | bary::StandardPropertyType* pErrorProp = nullptr); 326 | 327 | 328 | // pointers within `content` must remain valid until saving completed 329 | // creates a single output file where the content of "initContent" and "appendedContent" 330 | // are linearly stored in the order of appending. 331 | // - only works if info structs match except for "count" variable. 332 | // - uses internal copy for infos to account for aggregation of "elementCount" or "valueCount" 333 | // - internal temp allocation to account for shifts in 334 | // the various group "First" variables. Shift is based on the 335 | // prefix sum of the maxima (first + count) in previous files. 336 | bary::Result appendContent(const bary::ContentView* content, bary::StandardPropertyType* pErrorProp = nullptr); 337 | 338 | // pointers within views must remain valid until saving completed 339 | bary::Result appendContent(const bary::BasicView* basic, 340 | const bary::MeshView* mesh = nullptr, 341 | const bary::MiscView* misc = nullptr, 342 | bary::StandardPropertyType* pErrorProp = nullptr); 343 | 344 | 345 | // do not use standard properties covered by above functions here 346 | // this must cover all content, including appended and must be called 347 | // after appending was completed (if used at all) 348 | // pointers within `sinfo` must remain valid until saving completed 349 | void addCustomProperties(bary::PropertyStorageInfo sinfo); 350 | 351 | bary::Result save(const char* filename) const; 352 | bary::Result save(const std::string& filename) const { return save(filename.c_str()); } 353 | 354 | uint64_t computeFileSize() const; 355 | bary::Result save(uint64_t fileSize, void* fileData) const; 356 | 357 | private: 358 | struct SaverContext 359 | { 360 | const BarySaver* saver = nullptr; 361 | uint64_t fileSize = 0; 362 | uint8_t* fileData = nullptr; 363 | void* fileHandle = nullptr; 364 | 365 | bary::Result save(uint64_t offset, uint64_t size, const void* data); 366 | }; 367 | 368 | static bary::Result saverCallback(uint32_t propertyIdx, 369 | const bary::PropertyStorageInfo* propertyStorageInfo, 370 | uint64_t offset, 371 | uint64_t size, 372 | const void* data, 373 | bool isInfo, 374 | void* userData); 375 | 376 | static bary::Result fillPropertyStorageInfos(std::vector& props, 377 | const bary::ContentView* content, 378 | bary::StandardPropertyType* pErrorProp); 379 | 380 | bary::Result m_result = bary::Result::eErrorUnknown; 381 | std::vector m_props; 382 | // only used for appended 383 | std::vector> m_propsList; 384 | BaryContentInfo m_aggregatedInfo{}; 385 | }; 386 | 387 | ////////////////////////////////////////////////////////////////////////// 388 | 389 | class BaryFileHandle; 390 | 391 | struct BaryMemoryApi 392 | { 393 | void* (*alloc)(size_t size, void* userData); 394 | void (*free)(void* ptr, void* userData); 395 | void* userData = nullptr; 396 | }; 397 | 398 | struct BaryFileApi 399 | { 400 | bary::Result (*read)(const BaryMemoryApi* memoryApi, const BaryFileApi* fileApi, const char* path, size_t* size, void** data) = nullptr; 401 | void (*release)(const BaryMemoryApi* memoryApi, const BaryFileApi* fileApi, void* data) = nullptr; 402 | void* userData = nullptr; 403 | }; 404 | 405 | struct BaryFileOpenOptions 406 | { 407 | BaryMemoryApi memoryApi; 408 | BaryFileApi fileApi; 409 | }; 410 | 411 | 412 | // Class to open a file and provide quick access to standard 413 | // content properties. 414 | // The lifetime of all pointers depend on either the file handle 415 | // or the provided fileData in `open` 416 | class BaryFile 417 | { 418 | private: 419 | BaryFileHandle* m_handle = nullptr; 420 | 421 | bary::Result setupContent(bary::StandardPropertyType* outErrorType); 422 | 423 | public: 424 | bary::ContentView m_content; 425 | 426 | uint64_t m_fileSize = 0; 427 | // either points inside `m_handle` or provided `fileData` 428 | const void* m_fileData = nullptr; 429 | 430 | // the lifetime of the provided raw pointer is externally managed by developer and must be valid as long as BaryFile is open 431 | bary::Result open(size_t fileSize, const void* fileData, bary::StandardPropertyType* outErrorType = nullptr) 432 | { 433 | m_fileSize = fileSize; 434 | m_fileData = fileData; 435 | return setupContent(outErrorType); 436 | } 437 | bary::Result open(const char* name, const BaryFileOpenOptions* options = nullptr, bary::StandardPropertyType* outErrorType = nullptr); 438 | bary::Result open(const std::string& name, const BaryFileOpenOptions* options = nullptr, bary::StandardPropertyType* outErrorType = nullptr) 439 | { 440 | return open(name.c_str(), options, outErrorType); 441 | } 442 | void close(); 443 | 444 | bary::Result validate(bary::ValueSemanticType vtype, bary::StandardPropertyType* outErrorType = nullptr) const 445 | { 446 | return bary::baryContentIsValid(vtype, &m_content, outErrorType); 447 | } 448 | 449 | template 450 | const T* getValues() const 451 | { 452 | return reinterpret_cast(m_content.basic.values); 453 | } 454 | 455 | bool isCompressed() const 456 | { 457 | return m_content.basic.valuesInfo 458 | && (m_content.basic.valuesInfo->valueFormat == bary::Format::eDispC1_r11_unorm_block 459 | || m_content.basic.valuesInfo->valueFormat == bary::Format::eOpaC1_rx_uint_block); 460 | } 461 | 462 | bool hasProperty(bary::StandardPropertyType prop) const; 463 | 464 | const bary::BasicView& getBasic() const { return m_content.basic; } 465 | const bary::MeshView& getMesh() const { return m_content.mesh; } 466 | const bary::MiscView& getMisc() const { return m_content.misc; } 467 | const bary::ContentView& getContent() const { return m_content; } 468 | 469 | void fillBasicData(BaryBasicData& data) const { data.setData(m_content.basic); } 470 | void fillMeshData(BaryMeshData& data) const { data.setData(m_content.mesh); } 471 | void fillMiscData(BaryMiscData& data) const { data.setData(m_content.misc); } 472 | void fillContentData(BaryContentData& data) const { data.setData(m_content); } 473 | 474 | ~BaryFile() { close(); } 475 | }; 476 | 477 | ////////////////////////////////////////////////////////////////////////// 478 | 479 | struct BaryWUV_uint16 480 | { 481 | uint16_t w; 482 | uint16_t u; 483 | uint16_t v; 484 | }; 485 | 486 | using BaryUV_uint16 = bary::BaryUV_uint16; 487 | 488 | inline BaryWUV_uint16 makeWUV(uint32_t w, uint32_t u, uint32_t v) 489 | { 490 | return {uint16_t(w), uint16_t(u), uint16_t(v)}; 491 | } 492 | 493 | inline BaryUV_uint16 makeUV(uint32_t w, uint32_t u, uint32_t v) 494 | { 495 | return {uint16_t(u), uint16_t(v)}; 496 | } 497 | 498 | inline BaryWUV_uint16 makeWUV(bary::BaryUV_uint16 uv, uint32_t subdivLevel) 499 | { 500 | return {uint16_t((1u << subdivLevel) - uv.u - uv.v), uv.u, uv.v}; 501 | } 502 | 503 | // This class is useful as lookup table 504 | // of the storage indices for the different 505 | // subdivision levels and a provided value layout. 506 | // It also provides a minimal mesh representation that 507 | // these layouts create. 508 | 509 | class BaryLevelsMap 510 | { 511 | public: 512 | // 1< coordinates; // barycentric coordinates of micro-vertices 545 | std::vector triangles; // index topology of micro-triangles 546 | 547 | void getFloatCoord(size_t idx, float* vec) const 548 | { 549 | BaryWUV_uint16 coord = coordinates[idx]; 550 | float mul = 1.0f / float(1 << subdivLevel); 551 | vec[0] = float(coord.w) * mul; 552 | vec[1] = float(coord.u) * mul; 553 | vec[2] = float(coord.v) * mul; 554 | } 555 | 556 | uint32_t getCoordIndex(BaryWUV_uint16 coord) const 557 | { 558 | return baryValueLayoutGetIndex(layout, bary::ValueFrequency::ePerVertex, coord.u, coord.v, 0, subdivLevel); 559 | } 560 | 561 | uint32_t getBaryMax() const { return 1 << subdivLevel; } 562 | 563 | // returns indices vector for triangles taking the joining information 564 | // into account to collapse triangles at the edges for watertightness (avoid T-junctions). 565 | // decimateEdgeBits: set bit for those edges that gets half of the original segments 566 | // useDegenerated: keeps the collapsed triangles using degenerated triangle indices 567 | std::vector buildTrianglesWithCollapsedEdges(uint32_t decimateEdgeBits, bool useDegenerated = false) const; 568 | }; 569 | 570 | const Level& getLevel(uint32_t subdivLevel) const 571 | { 572 | assert(subdivLevel < uint32_t(m_levels.size())); 573 | return m_levels[subdivLevel]; 574 | } 575 | 576 | bool hasLevel(uint32_t subdivLevel) const { return subdivLevel < uint32_t(m_levels.size()); } 577 | 578 | uint32_t getNumLevels() const { return uint32_t(m_levels.size()); } 579 | 580 | bary::ValueLayout getLayout() const { return m_layout; } 581 | 582 | // using layout == bary::ValueLayout::eUndefined 583 | // will result in zero sized levels array 584 | 585 | void initialize(bary::ValueLayout layout, uint32_t maxSubdivLevel); 586 | 587 | BaryLevelsMap() {} 588 | BaryLevelsMap(bary::ValueLayout layout, uint32_t maxSubdivLevel) { initialize(layout, maxSubdivLevel); } 589 | 590 | private: 591 | std::vector m_levels; 592 | bary::ValueLayout m_layout = bary::ValueLayout::eUndefined; 593 | }; 594 | 595 | ////////////////////////////////////////////////////////////////////////// 596 | 597 | // pre-compute the compression-dependent splitting of 598 | // triangles into block-triangles 599 | 600 | class BarySplitTable 601 | { 602 | public: 603 | struct Entry 604 | { 605 | std::vector tris; 606 | 607 | uint32_t getCount() const { return static_cast(tris.size()); } 608 | 609 | void init(bary::BlockFormatDispC1 format, uint32_t baseSubdiv); 610 | }; 611 | 612 | const Entry& get(bary::BlockFormatDispC1 format, uint32_t level) const { return m_splits[getIndex(format, level)]; } 613 | 614 | // returns true on error 615 | // for now only bary::Format::eDispC1_r11_unorm_block supported 616 | bool init(bary::Format format, uint32_t maxLevel); 617 | 618 | private: 619 | static inline uint32_t getFormatIdx(bary::BlockFormatDispC1 format) { return uint32_t(format) - 1; } 620 | inline uint32_t getIndex(bary::BlockFormatDispC1 format, uint32_t level) const 621 | { 622 | assert(m_format == bary::Format::eDispC1_r11_unorm_block); 623 | return level * m_numFormats + getFormatIdx(format); 624 | } 625 | 626 | std::vector m_splits; 627 | bary::Format m_format{}; 628 | uint32_t m_numFormats{}; 629 | }; 630 | 631 | } // namespace baryutils 632 | -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | int main(int argc, const char** args) 24 | { 25 | return EXIT_SUCCESS; 26 | } 27 | 28 | -------------------------------------------------------------------------------- /tool/README.md: -------------------------------------------------------------------------------- 1 | # bary_tool 2 | 3 | commandline tool, convert versions etc. 4 | 5 | `bary_tool ... commands ...` 6 | 7 | **Supported versions** 8 | - v100 9 | 10 | **List of commands** 11 | - `-appendonsave `: appends another file when save is triggered (only standard properties) 12 | - `-save `: saves new file, but only preserves standard properties. 13 | 14 | **Example output** 15 | ``` 16 | input file: test_c.bary 17 | Version: 100 18 | 19 | File properties 20 | property 0: eGroups 21 | property 1: eTriangles 22 | property 2: eValues 23 | property 3: eTriangleMinMaxs 24 | property 4: eHistogramEntries 25 | property 5: eGroupHistograms 26 | property 6: eMeshDisplacementDirectionBounds 27 | 28 | Validation 29 | property eGroups 30 | property eTriangles 31 | property eValues 32 | property eTriangleMinMaxs 33 | property eHistogramEntries 34 | property eGroupHistograms 35 | property eMeshDisplacementDirectionBounds 36 | all passed 37 | 38 | Globals 39 | triangles 6 40 | groups 1 41 | 42 | ValueInfo 43 | valueCount 384 44 | valueByteSize 1 45 | valueByteAlign 128 46 | valueFormat eDispC1_r11_unorm_block 47 | valueLayout eTriangleBirdCurve 48 | valueFrequency ePerTriangle 49 | 50 | Group 0: 51 | triangleCount 6 52 | triangleFirst 0 53 | valueCount 384 54 | valueFirst 0 55 | minSubdivLevel 3 56 | maxSubdivLevel 3 57 | bias {0.000000, 0.000000, 0.000000, 0.000000} 58 | scale {1.000000, 0.000000, 0.000000, 0.000000} 59 | 60 | computed primitive subdivlevel histogram: 61 | subdiv 0: 0 62 | subdiv 1: 0 63 | subdiv 2: 0 64 | subdiv 3: 6 65 | computed primitive blockformat histogram: 66 | blockformat 1: 6 67 | blockformat 2: 0 68 | blockformat 3: 0 69 | file property block format histogram: 70 | subdiv 3 blockformat 1: 6 71 | ``` 72 | 73 | -------------------------------------------------------------------------------- /tool/bary_tool.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | #include "filemapping.hpp" 24 | 25 | 26 | static bary::Result baryutils_read(const baryutils::BaryMemoryApi* memory_options, 27 | const baryutils::BaryFileApi* file_options, 28 | const char* path, 29 | size_t* size, 30 | void** data) 31 | { 32 | FileMappingList* mappings = (FileMappingList*)file_options->userData; 33 | if(mappings->open(path, size, data)) 34 | { 35 | return bary::Result::eSuccess; 36 | } 37 | 38 | return bary::Result::eErrorIO; 39 | } 40 | 41 | static void baryutils_release(const baryutils::BaryMemoryApi* memory_options, const baryutils::BaryFileApi* file_options, void* data) 42 | { 43 | FileMappingList* mappings = (FileMappingList*)file_options->userData; 44 | mappings->close(data); 45 | } 46 | 47 | static bool checkAndPrintError(const char* operation, 48 | bary::Result result, 49 | bary::StandardPropertyType errorProp = bary::StandardPropertyType::eUnknown) 50 | { 51 | if(result != bary::Result::eSuccess) 52 | { 53 | printf("error in '%s': %s (prop: %s)\n", operation, bary::baryResultGetName(result), 54 | errorProp == bary::StandardPropertyType::eUnknown ? "-" : bary::baryStandardPropertyGetName(errorProp)); 55 | return false; 56 | } 57 | 58 | return true; 59 | } 60 | 61 | 62 | bary::Result loadConverted(baryutils::BaryContentData& baryData, const baryutils::BaryFileOpenOptions& options, const char* filename, uint32_t versionNumber) 63 | { 64 | // might react on versionNumber here 65 | return bary::Result::eErrorVersion; 66 | } 67 | 68 | void printHelp() 69 | { 70 | bary::VersionIdentifier id = bary::baryGetCurrentVersionIdentifier(); 71 | uint32_t currentVersion; 72 | bary::baryVersionIdentifierGetVersion(&id, ¤tVersion); 73 | 74 | printf("bary_tool ...commands...\n"); 75 | printf("supported file versions:\n"); 76 | printf("------------------------\n"); 77 | printf("%d (current)\n", currentVersion); 78 | printf("commands:\n"); 79 | printf("---------\n"); 80 | printf("-appendonsave : appends another file when save is triggered (only standard properties)\n"); 81 | printf("-save : saves file (only standard properties)\n"); 82 | } 83 | 84 | bool validateContent(const bary::ContentView* content, const char* what) 85 | { 86 | bary::Result result; 87 | bary::StandardPropertyType errorProp = bary::StandardPropertyType::eUnknown; 88 | 89 | printf("%s validation\n", what); 90 | 91 | uint32_t propStorageCount = bary::baryContentComputePropertyCount(content); 92 | std::vector propStorages(propStorageCount); 93 | result = bary::baryContentSetupProperties(content, propStorageCount, propStorages.data()); 94 | if(!checkAndPrintError("validation", result)) 95 | { 96 | return false; 97 | } 98 | 99 | for(uint32_t i = 0; i < propStorageCount; i++) 100 | { 101 | bary::PropertyIdentifier identifier = propStorages[i].identifier; 102 | bary::StandardPropertyType prop = bary::baryPropertyGetStandardType(identifier); 103 | printf(" property %s\n", bary::baryStandardPropertyGetName(prop)); 104 | } 105 | 106 | result = bary::baryValidateStandardProperties(propStorageCount, propStorages.data(), ~uint64_t(0), &errorProp); 107 | if(!checkAndPrintError("validation", result, errorProp)) 108 | { 109 | return false; 110 | } 111 | printf(" all passed\n\n"); 112 | 113 | return true; 114 | } 115 | 116 | extern "C" int main(int argc, const char** argv) 117 | { 118 | if(argc < 2) 119 | { 120 | printHelp(); 121 | return 0; 122 | } 123 | 124 | const char* filename = argv[1]; 125 | 126 | printf("input file: %s\n", filename); 127 | 128 | uint32_t versionNumber = 0; 129 | 130 | { 131 | FILE* file; 132 | #ifdef WIN32 133 | if(fopen_s(&file, filename, "rb")) 134 | { 135 | #else 136 | if((file = fopen(filename, "rb")) == nullptr) 137 | { 138 | #endif 139 | printf("could not open file\n"); 140 | return -1; 141 | } 142 | 143 | bary::VersionIdentifier id; 144 | if(!fread(&id, sizeof(id), 1, file)) 145 | { 146 | printf("could not read version identifier\n"); 147 | return -1; 148 | } 149 | fclose(file); 150 | 151 | if(bary::baryVersionIdentifierGetVersion(&id, &versionNumber) != bary::Result::eSuccess) 152 | { 153 | printf("could not read version number\n"); 154 | return -1; 155 | } 156 | 157 | printf("Version: %d\n\n", versionNumber); 158 | } 159 | 160 | FileMappingList mappingList; 161 | 162 | std::vector appendFilenames; 163 | std::vector> appendFiles; 164 | 165 | baryutils::BaryFile bfile; 166 | bary::ContentView content; 167 | bary::BasicView basicView; 168 | bary::MeshView meshView; 169 | bary::MiscView miscView; 170 | baryutils::BaryContentData baryConvert; 171 | 172 | bary::StandardPropertyType errorProp = bary::StandardPropertyType::eUnknown; 173 | baryutils::BaryFileOpenOptions openOptions = {0}; 174 | openOptions.fileApi.userData = &mappingList; 175 | openOptions.fileApi.read = baryutils_read; 176 | openOptions.fileApi.release = baryutils_release; 177 | 178 | bary::Result result = bfile.open(filename, &openOptions, &errorProp); 179 | checkAndPrintError("open file", result, errorProp); 180 | 181 | if(result == bary::Result::eErrorVersion) 182 | { 183 | bfile.close(); 184 | 185 | printf("attempting to convert older version\n"); 186 | result = loadConverted(baryConvert, openOptions, filename, versionNumber); 187 | if(!checkAndPrintError("convert file", result)) 188 | { 189 | return -1; 190 | } 191 | 192 | content.basic = baryConvert.basic.getView(); 193 | content.mesh = baryConvert.mesh.getView(); 194 | content.misc = baryConvert.misc.getView(); 195 | } 196 | else if(result == bary::Result::eSuccess) 197 | { 198 | content.basic = bfile.getBasic(); 199 | content.mesh = bfile.getMesh(); 200 | content.misc = bfile.getMisc(); 201 | 202 | // list all properties 203 | uint64_t propCount; 204 | printf("File properties\n"); 205 | const bary::PropertyInfo* propInfos = bary::baryDataGetAllPropertyInfos(bfile.m_fileSize, bfile.m_fileData, &propCount); 206 | for(uint16_t i = 0; i < propCount; i++) 207 | { 208 | bary::PropertyIdentifier identifier = propInfos[i].identifier; 209 | printf(" property %d: ", i); 210 | bary::StandardPropertyType prop = bary::baryPropertyGetStandardType(identifier); 211 | if(prop == bary::StandardPropertyType::eUnknown) 212 | { 213 | printf("unknown identifier {0x%x, 0x%x, 0x%x, 0x%x}\n", identifier.uuid4[0], identifier.uuid4[1], 214 | identifier.uuid4[2], identifier.uuid4[3]); 215 | } 216 | else 217 | { 218 | printf("%s\n", bary::baryStandardPropertyGetName(prop)); 219 | } 220 | } 221 | printf("\n"); 222 | } 223 | else 224 | { 225 | return -1; 226 | } 227 | 228 | if (!validateContent(&content, "input")) 229 | { 230 | return -1; 231 | } 232 | 233 | printf("Globals\n"); 234 | printf(" triangles %10d\n", content.basic.trianglesCount); 235 | printf(" groups %10d\n", content.basic.groupsCount); 236 | printf("\n"); 237 | 238 | auto valuesInfo = content.basic.valuesInfo; 239 | printf("ValueInfo\n"); 240 | printf(" valueCount %10d\n", valuesInfo->valueCount); 241 | printf(" valueByteSize %10d\n", valuesInfo->valueByteSize); 242 | printf(" valueByteAlign %10d\n", valuesInfo->valueByteAlignment); 243 | printf(" valueFormat %s\n", bary::baryFormatGetName(valuesInfo->valueFormat)); 244 | printf(" valueLayout %s\n", bary::baryValueLayoutGetName(valuesInfo->valueLayout)); 245 | printf(" valueFrequency %s\n", bary::baryValueFrequencyGetName(valuesInfo->valueFrequency)); 246 | printf("\n"); 247 | 248 | // iterate groups 249 | for(uint32_t g = 0; g < content.basic.groupsCount; g++) 250 | { 251 | const bary::Group* group = content.basic.groups + g; 252 | 253 | printf("Group %d:\n", g); 254 | printf(" triangleCount %10d\n", group->triangleCount); 255 | printf(" triangleFirst %10d\n", group->triangleFirst); 256 | printf(" valueCount %10d\n", group->valueCount); 257 | printf(" valueFirst %10d\n", group->valueFirst); 258 | printf(" minSubdivLevel %10d\n", group->minSubdivLevel); 259 | printf(" maxSubdivLevel %10d\n", group->maxSubdivLevel); 260 | printf(" bias {%f, %f, %f, %f}\n", group->floatBias.r, group->floatBias.g, group->floatBias.b, 261 | group->floatBias.a); 262 | printf(" scale {%f, %f, %f, %f}\n", group->floatScale.r, group->floatScale.g, group->floatScale.b, 263 | group->floatScale.a); 264 | printf("\n"); 265 | 266 | // hack 267 | std::vector histoLevel(group->maxSubdivLevel + 1, 0); 268 | const uint32_t histoBlockEntries = 4; 269 | std::vector histoBlock(histoBlockEntries, 0); 270 | 271 | for(uint32_t i = 0; i < group->triangleCount; i++) 272 | { 273 | const bary::Triangle* tri = content.basic.triangles + (group->triangleFirst + i); 274 | histoLevel[tri->subdivLevel]++; 275 | histoBlock[tri->blockFormat % histoBlockEntries]++; 276 | } 277 | 278 | printf(" computed primitive subdivlevel histogram:\n"); 279 | for(uint32_t i = 0; i < group->maxSubdivLevel + 1; i++) 280 | { 281 | printf(" subdiv %2d: %9d\n", i, histoLevel[i]); 282 | } 283 | 284 | printf(" computed primitive blockformat histogram:\n"); 285 | for(uint32_t i = 1; i < histoBlockEntries; i++) 286 | { 287 | printf(" blockformat %2d: %9d\n", i, histoBlock[i]); 288 | } 289 | 290 | if(content.basic.histogramEntries && content.basic.groupHistogramRanges) 291 | { 292 | printf(" file property block format histogram:\n"); 293 | for(uint32_t i = 0; i < content.basic.groupHistogramRanges[g].entryCount; i++) 294 | { 295 | const bary::HistogramEntry* entry = 296 | content.basic.histogramEntries + (content.basic.groupHistogramRanges[g].entryFirst + i); 297 | printf(" subdiv %2d blockformat %2d: %9d\n", entry->subdivLevel, entry->blockFormat, entry->count); 298 | } 299 | } 300 | 301 | printf("\n"); 302 | } 303 | printf("\n"); 304 | 305 | for(int i = 2; i < argc; i++) 306 | { 307 | if(strcmp(argv[i], "-appendonsave") == 0 && i + 1 < argc) 308 | { 309 | i++; 310 | const char* appendname = argv[i]; 311 | 312 | std::unique_ptr bfileAppend = std::make_unique(); 313 | 314 | printf("appendonsave file: %s\n", appendname); 315 | bary::Result result = bfileAppend->open(appendname, &openOptions, &errorProp); 316 | if (!checkAndPrintError("open appendonsave file", result, errorProp)) 317 | { 318 | return -1; 319 | } 320 | 321 | bary::ContentView appendContent = bfileAppend->getContent(); 322 | if(!validateContent(&appendContent, "appendonsave")) 323 | { 324 | return -1; 325 | } 326 | 327 | appendFiles.push_back(std::move(bfileAppend)); 328 | appendFilenames.push_back(appendname); 329 | } 330 | else if(strcmp(argv[i], "-save") == 0 && i + 1 < argc) 331 | { 332 | i++; 333 | const char* savename = argv[i]; 334 | 335 | bary::StandardPropertyType errorProp = bary::StandardPropertyType::eUnknown; 336 | baryutils::BarySaver saver; 337 | printf("save file: %s\n", savename); 338 | result = saver.initContent(&content, &errorProp); 339 | if(!checkAndPrintError("save init", result, errorProp)) 340 | { 341 | return -1; 342 | } 343 | 344 | if (appendFiles.size()) 345 | { 346 | for (size_t a = 0; a < appendFiles.size(); a++) 347 | { 348 | printf("save append %s\n", appendFilenames[a]); 349 | 350 | bary::ContentView appendContent = appendFiles[a]->getContent(); 351 | result = saver.appendContent(&appendContent, &errorProp); 352 | if(!checkAndPrintError("save append", result, errorProp)) 353 | { 354 | return -1; 355 | } 356 | } 357 | } 358 | 359 | result = saver.save(savename); 360 | if(!checkAndPrintError("save file", result)) 361 | { 362 | return -1; 363 | } 364 | printf("successfully saved\n"); 365 | } 366 | } 367 | 368 | return 0; 369 | } 370 | -------------------------------------------------------------------------------- /tool/filemapping.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include "filemapping.hpp" 19 | #include 20 | 21 | #if defined(LINUX) 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #endif 29 | 30 | #if defined(_WIN32) 31 | #define WIN32_LEAN_AND_MEAN 32 | #include 33 | 34 | inline DWORD HIDWORD(size_t x) 35 | { 36 | return (DWORD)(x >> 32); 37 | } 38 | inline DWORD LODWORD(size_t x) 39 | { 40 | return (DWORD)x; 41 | } 42 | #endif 43 | 44 | 45 | bool FileMapping::open(const char* fileName, MappingType mappingType, size_t fileSize) 46 | { 47 | if(!g_pageSize) 48 | { 49 | #if defined(_WIN32) 50 | SYSTEM_INFO si; 51 | GetSystemInfo(&si); 52 | g_pageSize = (size_t)si.dwAllocationGranularity; 53 | #elif defined(LINUX) 54 | g_pageSize = (size_t)getpagesize(); 55 | #endif 56 | } 57 | 58 | m_mappingType = mappingType; 59 | 60 | if(mappingType == MAPPING_READOVERWRITE) 61 | { 62 | assert(fileSize); 63 | m_fileSize = fileSize; 64 | m_mappingSize = ((fileSize + g_pageSize - 1) / g_pageSize) * g_pageSize; 65 | 66 | // check if the current process is allowed to save a file of that size 67 | #if defined(_WIN32) 68 | TCHAR dir[MAX_PATH + 1]; 69 | BOOL success = FALSE; 70 | ULARGE_INTEGER numFreeBytes; 71 | 72 | DWORD length = GetVolumePathName(fileName, dir, MAX_PATH + 1); 73 | 74 | if(length > 0) 75 | { 76 | success = GetDiskFreeSpaceEx(dir, NULL, NULL, &numFreeBytes); 77 | } 78 | 79 | m_isValid = (!!success) && (m_mappingSize <= numFreeBytes.QuadPart); 80 | #elif defined(LINUX) 81 | struct rlimit rlim; 82 | getrlimit(RLIMIT_FSIZE, &rlim); 83 | m_isValid = (m_mappingSize <= rlim.rlim_cur); 84 | #endif 85 | if(!m_isValid) 86 | { 87 | return false; 88 | } 89 | } 90 | 91 | #if defined(_WIN32) 92 | m_win32.file = mappingType == MAPPING_READONLY ? 93 | CreateFile(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL) : 94 | CreateFile(fileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 95 | 96 | m_isValid = (m_win32.file != INVALID_HANDLE_VALUE); 97 | if(m_isValid) 98 | { 99 | if(mappingType == MAPPING_READONLY) 100 | { 101 | DWORD sizeHi = 0; 102 | DWORD sizeLo = GetFileSize(m_win32.file, &sizeHi); 103 | m_mappingSize = (static_cast(sizeHi) << 32) | sizeLo; 104 | m_fileSize = m_mappingSize; 105 | } 106 | 107 | m_win32.fileMapping = CreateFileMapping(m_win32.file, NULL, mappingType == MAPPING_READONLY ? PAGE_READONLY : PAGE_READWRITE, 108 | HIDWORD(m_mappingSize), LODWORD(m_mappingSize), NULL); 109 | 110 | m_isValid = (m_win32.fileMapping != NULL); 111 | if(m_isValid) 112 | { 113 | m_mappingPtr = MapViewOfFile(m_win32.fileMapping, mappingType == MAPPING_READONLY ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS, 114 | HIDWORD(0), LODWORD(0), (SIZE_T)0); 115 | if (!m_mappingPtr) 116 | { 117 | #if 0 118 | DWORD err = GetLastError(); 119 | #endif 120 | CloseHandle(m_win32.file); 121 | m_isValid = false; 122 | } 123 | } 124 | else 125 | { 126 | CloseHandle(m_win32.file); 127 | } 128 | } 129 | #elif defined(LINUX) 130 | m_unix.file = mappingType == MAPPING_READONLY ? ::open(fileName, O_RDONLY) : ::open(fileName, O_RDWR | O_CREAT | O_TRUNC, 0666); 131 | 132 | m_isValid = (m_unix.file != -1); 133 | if(m_isValid) 134 | { 135 | if(mappingType == MAPPING_READONLY) 136 | { 137 | struct stat s; 138 | m_isValid &= (fstat(m_unix.file, &s) >= 0); 139 | m_mappingSize = s.st_size; 140 | } 141 | else 142 | { 143 | // make file large enough to hold the complete scene 144 | m_isValid &= (lseek(m_unix.file, m_mappingSize - 1, SEEK_SET) >= 0); 145 | m_isValid &= (write(m_unix.file, "", 1) >= 0); 146 | m_isValid &= (lseek(m_unix.file, 0, SEEK_SET) >= 0); 147 | } 148 | m_fileSize = m_mappingSize; 149 | if(m_isValid) 150 | { 151 | m_mappingPtr = mmap(0, m_mappingSize, mappingType == MAPPING_READONLY ? PROT_READ : (PROT_READ | PROT_WRITE), 152 | MAP_SHARED, m_unix.file, 0); 153 | m_isValid = (m_mappingPtr != MAP_FAILED); 154 | } 155 | if (!m_isValid) 156 | { 157 | ::close(m_unix.file); 158 | m_unix.file = -1; 159 | } 160 | } 161 | #endif 162 | return m_isValid; 163 | } 164 | 165 | void FileMapping::close() 166 | { 167 | if(m_isValid) 168 | { 169 | #if defined(_WIN32) 170 | assert((m_win32.file != INVALID_HANDLE_VALUE) && (m_win32.fileMapping != NULL)); 171 | 172 | UnmapViewOfFile(m_mappingPtr); 173 | CloseHandle(m_win32.fileMapping); 174 | 175 | if(m_mappingType == MAPPING_READOVERWRITE) 176 | { 177 | // truncate file to minimum size 178 | // To work with 64-bit file pointers, you can declare a LONG, treat it as the upper half 179 | // of the 64-bit file pointer, and pass its address in lpDistanceToMoveHigh. This means 180 | // you have to treat two different variables as a logical unit, which is error-prone. 181 | // The problems can be ameliorated by using the LARGE_INTEGER structure to create a 64-bit 182 | // value and passing the two 32-bit values by means of the appropriate elements of the union. 183 | // (see msdn documentation on SetFilePointer) 184 | LARGE_INTEGER li; 185 | li.QuadPart = (__int64)m_fileSize; 186 | SetFilePointer(m_win32.file, li.LowPart, &li.HighPart, FILE_BEGIN); 187 | 188 | SetEndOfFile(m_win32.file); 189 | } 190 | CloseHandle(m_win32.file); 191 | 192 | m_mappingPtr = nullptr; 193 | m_win32.fileMapping = nullptr; 194 | m_win32.file = nullptr; 195 | 196 | #elif defined(LINUX) 197 | assert(m_unix.file != -1); 198 | 199 | munmap(m_mappingPtr, m_mappingSize); 200 | ::close(m_unix.file); 201 | 202 | m_mappingPtr = nullptr; 203 | m_unix.file = -1; 204 | #endif 205 | 206 | m_isValid = false; 207 | } 208 | } 209 | 210 | size_t FileMapping::g_pageSize = 0; 211 | 212 | -------------------------------------------------------------------------------- /tool/filemapping.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #pragma once 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | class FileMapping 27 | { 28 | public: 29 | 30 | FileMapping(FileMapping&& other) noexcept 31 | { 32 | this->operator=(std::move(other)); 33 | }; 34 | 35 | FileMapping& operator=(FileMapping&& other) noexcept 36 | { 37 | m_isValid = other.m_isValid; 38 | m_fileSize = other.m_fileSize; 39 | m_mappingType = other.m_mappingType; 40 | m_mappingPtr = other.m_mappingPtr; 41 | m_mappingSize = other.m_mappingSize; 42 | #ifdef _WIN32 43 | m_win32.file = other.m_win32.file; 44 | m_win32.fileMapping = other.m_win32.fileMapping; 45 | other.m_win32.file = nullptr; 46 | other.m_win32.fileMapping = nullptr; 47 | #else 48 | m_unix.file = other.m_unix.file; 49 | other.m_unix.file = -1; 50 | #endif 51 | other.m_isValid = false; 52 | other.m_mappingPtr = nullptr; 53 | 54 | return *this; 55 | } 56 | 57 | FileMapping(const FileMapping&) = delete; 58 | FileMapping& operator=(const FileMapping& other) = delete; 59 | FileMapping() {} 60 | 61 | ~FileMapping() { close(); } 62 | 63 | enum MappingType 64 | { 65 | MAPPING_READONLY, // opens existing file for read-only access 66 | MAPPING_READOVERWRITE, // creates new file with read/write access, overwriting existing files 67 | }; 68 | 69 | // fileSize only for write access 70 | bool open(const char* filename, MappingType mappingType, size_t fileSize = 0); 71 | void close(); 72 | 73 | const void* data() const { return m_mappingPtr; } 74 | void* data() { return m_mappingPtr; } 75 | size_t size() const { return m_mappingSize; } 76 | bool valid() const { return m_isValid; } 77 | 78 | protected: 79 | static size_t g_pageSize; 80 | 81 | #ifdef _WIN32 82 | struct 83 | { 84 | void* file = nullptr; 85 | void* fileMapping = nullptr; 86 | } m_win32; 87 | #else 88 | struct 89 | { 90 | int file = -1; 91 | } m_unix; 92 | #endif 93 | 94 | bool m_isValid = false; 95 | size_t m_fileSize = 0; 96 | MappingType m_mappingType = MappingType::MAPPING_READONLY; 97 | void* m_mappingPtr = nullptr; 98 | size_t m_mappingSize = 0; 99 | }; 100 | 101 | // convenience types 102 | class FileReadMapping : private FileMapping 103 | { 104 | public: 105 | bool open(const char* filename) { return FileMapping::open(filename, MAPPING_READONLY, 0); } 106 | void close() { FileMapping::close(); } 107 | const void* data() const { return m_mappingPtr; } 108 | size_t size() const { return m_fileSize; } 109 | bool valid() const { return m_isValid; } 110 | }; 111 | 112 | class FileReadOverWriteMapping : private FileMapping 113 | { 114 | public: 115 | bool open(const char* filename, size_t fileSize) 116 | { 117 | return FileMapping::open(filename, MAPPING_READOVERWRITE, fileSize); 118 | } 119 | void close() { FileMapping::close(); } 120 | void* data() { return m_mappingPtr; } 121 | size_t size() const { return m_fileSize; } 122 | bool valid() const { return m_isValid; } 123 | }; 124 | 125 | 126 | struct FileMappingList 127 | { 128 | struct Entry 129 | { 130 | FileReadMapping mapping; 131 | int64_t refCount = 1; 132 | }; 133 | std::unordered_map m_nameToMapping; 134 | std::unordered_map m_dataToName; 135 | #ifdef _DEBUG 136 | int64_t m_openBias = 0; 137 | #endif 138 | 139 | bool open(const char* path, size_t* size, void** data) 140 | { 141 | #ifdef _DEBUG 142 | m_openBias++; 143 | #endif 144 | 145 | std::string pathStr(path); 146 | 147 | auto it = m_nameToMapping.find(pathStr); 148 | if(it != m_nameToMapping.end()) 149 | { 150 | *data = const_cast(it->second.mapping.data()); 151 | *size = it->second.mapping.size(); 152 | it->second.refCount++; 153 | return true; 154 | } 155 | 156 | Entry entry; 157 | if(entry.mapping.open(path)) 158 | { 159 | const void* mappingData = entry.mapping.data(); 160 | *data = const_cast(mappingData); 161 | *size = entry.mapping.size(); 162 | m_dataToName.insert({mappingData, pathStr}); 163 | m_nameToMapping.insert({pathStr, std::move(entry)}); 164 | return true; 165 | } 166 | 167 | return false; 168 | } 169 | 170 | void close(void* data) 171 | { 172 | #ifdef _DEBUG 173 | m_openBias--; 174 | #endif 175 | auto itName = m_dataToName.find(data); 176 | if(itName != m_dataToName.end()) 177 | { 178 | auto itMapping = m_nameToMapping.find(itName->second); 179 | if(itMapping != m_nameToMapping.end()) 180 | { 181 | itMapping->second.refCount--; 182 | 183 | if(!itMapping->second.refCount) 184 | { 185 | m_nameToMapping.erase(itMapping); 186 | m_dataToName.erase(itName); 187 | } 188 | } 189 | } 190 | } 191 | 192 | ~FileMappingList() 193 | { 194 | #ifdef _DEBUG 195 | assert(m_openBias == 0 && "open/close bias wrong"); 196 | #endif 197 | assert(m_nameToMapping.empty() && m_dataToName.empty() && "not all opened files were closed"); 198 | } 199 | }; 200 | 201 | --------------------------------------------------------------------------------