diff --git a/.github/workflows/inno_setup b/.github/workflows/inno_setup new file mode 100644 index 000000000..0bfef0cd3 --- /dev/null +++ b/.github/workflows/inno_setup @@ -0,0 +1,51 @@ +name: Build Windows Installer +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] +jobs: + build: + name: Build the Inno Setup Installer + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - uses: lukka/get-cmake@latest + - uses: ilammy/msvc-dev-cmd@v1 + + + - name: Cache DX SDK + id: dxcache + uses: actions/cache@v4 + with: + path: '${{ github.workspace }}/DX_SDK' + key: ${{ runner.os }}-DX_SDK + restore-keys: | + ${{ runner.os }}-DX_SDK + + - name: Download DXSetup + run: | + curl -s -o DXSDK_Jun10.exe --location https://download.microsoft.com/download/A/E/7/AE743F1F-632B-4809-87A9-AA1BB3458E31/DXSDK_Jun10.exe + cmd.exe /c start /wait .\DXSDK_Jun10.exe /U /O /F /S /P "${{ github.workspace }}\DX_SDK" + + - name: Set Windows specific CMake arguments + id: windows_extra_cmake_args + run: echo "::set-output name=args::-DASSIMP_BUILD_ASSIMP_TOOLS=1 -DASSIMP_BUILD_ASSIMP_VIEW=1 -DASSIMP_BUILD_ZLIB=1" + + - name: configure and build + uses: lukka/run-cmake@v3 + env: + DXSDK_DIR: '${{ github.workspace }}/DX_SDK' + + with: + cmakeListsOrSettingsJson: CMakeListsTxtAdvanced + cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt' + cmakeAppendedArgs: '-GNinja -DCMAKE_BUILD_TYPE=Release ${{ steps.windows_extra_cmake_args.outputs.args }} ${{ steps.hunter_extra_cmake_args.outputs.args }}' + buildWithCMakeArgs: '--parallel 24 -v' + buildDirectory: '${{ github.workspace }}/build/' + + - name: Compile .ISS to .EXE Installer + uses: Minionguyjpro/Inno-Setup-Action@v1.2.2 + with: + path: packaging/windows-innosetup/script_x64.iss + options: /O+ diff --git a/.gitignore b/.gitignore index 09d631ee8..c8fa089db 100644 --- a/.gitignore +++ b/.gitignore @@ -120,3 +120,7 @@ tools/assimp_qt_viewer/ui_mainwindow.h #Generated directory generated/* + +# 3rd party cloned repos/tarballs etc +# tinyusdz repo, automatically cloned via CMake +contrib/tinyusdz/autoclone diff --git a/CMakeLists.txt b/CMakeLists.txt index 670f29f86..d160f48b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,13 @@ SET(CMAKE_POLICY_DEFAULT_CMP0092 NEW) CMAKE_MINIMUM_REQUIRED( VERSION 3.22 ) +# Experimental USD importer: disabled, need to opt-in +# Note: assimp github PR automatic checks will fail the PR due to compiler warnings in +# the external, 3rd party tinyusdz code which isn't technically part of the PR since it's +# auto-cloned during build; so MUST disable the feature or the PR will be rejected +option(ASSIMP_BUILD_USD_IMPORTER "Enable USD file import" off) +option(ASSIMP_BUILD_USD_VERBOSE_LOGS "Enable verbose USD import debug logging" off) + # Disabled importers: m3d for 5.1 or later ADD_DEFINITIONS( -DASSIMP_BUILD_NO_M3D_IMPORTER) ADD_DEFINITIONS( -DASSIMP_BUILD_NO_M3D_EXPORTER) @@ -55,7 +62,7 @@ IF(ASSIMP_HUNTER_ENABLED) add_definitions(-DASSIMP_USE_HUNTER) ENDIF() -PROJECT(Assimp VERSION 5.4.0) +PROJECT(Assimp VERSION 5.4.1) # All supported options ############################################### @@ -147,7 +154,7 @@ IF (WIN32) IF(MSVC) OPTION( ASSIMP_INSTALL_PDB - "Install MSVC debug files." + "Create MSVC debug symbol files and add to Install target." ON ) IF(NOT (MSVC_VERSION LESS 1900)) # Multibyte character set has been deprecated since at least MSVC2015 (possibly earlier) @@ -262,7 +269,7 @@ IF ((CMAKE_C_COMPILER_ID MATCHES "GNU") AND NOT MINGW AND NOT HAIKU) IF(NOT ASSIMP_HUNTER_ENABLED) SET(CMAKE_POSITION_INDEPENDENT_CODE ON) ENDIF() - + IF(CMAKE_CXX_COMPILER_VERSION GREATER_EQUAL 13) MESSAGE(STATUS "GCC13 detected disabling \"-Wdangling-reference\" in Cpp files as it appears to be a false positive") ADD_COMPILE_OPTIONS("$<$:-Wno-dangling-reference>") @@ -284,16 +291,23 @@ ELSEIF(MSVC) ELSE() # msvc ADD_COMPILE_OPTIONS(/MP /bigobj) ENDIF() - + # disable "elements of array '' will be default initialized" warning on MSVC2013 IF(MSVC12) - ADD_COMPILE_OPTIONS(/wd4351) + ADD_COMPILE_OPTIONS(/wd4351) ENDIF() # supress warning for double to float conversion if Double precision is activated - ADD_COMPILE_OPTIONS(/wd4244) + ADD_COMPILE_OPTIONS(/wd4244) SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_DEBUG /Zi /Od") - SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") - SET(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG:FULL /PDBALTPATH:%_PDB% /OPT:REF /OPT:ICF") + # Allow user to disable PDBs + if(ASSIMP_INSTALL_PDB) + SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi") + SET(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG:FULL /PDBALTPATH:%_PDB% /OPT:REF /OPT:ICF") + elseif((GENERATOR_IS_MULTI_CONFIG) OR (CMAKE_BUILD_TYPE MATCHES Release)) + message("-- MSVC PDB generation disabled. Release binary will not be debuggable.") + endif() + # Source code is encoded in UTF-8 + ADD_COMPILE_OPTIONS(/source-charset:utf-8) ELSEIF (CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) IF(NOT ASSIMP_HUNTER_ENABLED) SET(CMAKE_POSITION_INDEPENDENT_CODE ON) @@ -330,7 +344,7 @@ ENDIF() IF (ASSIMP_COVERALLS) MESSAGE(STATUS "Coveralls enabled") - + INCLUDE(Coveralls) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") @@ -338,7 +352,7 @@ ENDIF() IF (ASSIMP_ASAN) MESSAGE(STATUS "AddressSanitizer enabled") - + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address") ENDIF() @@ -452,18 +466,20 @@ configure_package_config_file( INSTALL_DESTINATION "${CONFIG_INSTALL_DIR}" ) -install( - FILES "${PROJECT_CONFIG}" "${VERSION_CONFIG}" - DESTINATION "${CONFIG_INSTALL_DIR}" - COMPONENT ${LIBASSIMP-DEV_COMPONENT} -) +if(ASSIMP_INSTALL) + install( + FILES "${PROJECT_CONFIG}" "${VERSION_CONFIG}" + DESTINATION "${CONFIG_INSTALL_DIR}" + COMPONENT ${LIBASSIMP-DEV_COMPONENT} + ) -install( - EXPORT "${TARGETS_EXPORT_NAME}" - NAMESPACE "${NAMESPACE}" - DESTINATION "${CONFIG_INSTALL_DIR}" - COMPONENT ${LIBASSIMP-DEV_COMPONENT} -) + install( + EXPORT "${TARGETS_EXPORT_NAME}" + NAMESPACE "${NAMESPACE}" + DESTINATION "${CONFIG_INSTALL_DIR}" + COMPONENT ${LIBASSIMP-DEV_COMPONENT} + ) +endif() IF( ASSIMP_BUILD_DOCS ) ADD_SUBDIRECTORY(doc) @@ -481,7 +497,7 @@ IF(ASSIMP_HUNTER_ENABLED) set(ASSIMP_BUILD_MINIZIP TRUE) ELSE() # If the zlib is already found outside, add an export in case assimpTargets can't find it. - IF( ZLIB_FOUND ) + IF( ZLIB_FOUND AND ASSIMP_INSTALL) INSTALL( TARGETS zlib zlibstatic EXPORT "${TARGETS_EXPORT_NAME}") ENDIF() @@ -634,7 +650,7 @@ ELSE() IF ( ASSIMP_BUILD_DRACO ) # Primarily for glTF v2 # Enable Draco glTF feature set - set(DRACO_GLTF ON CACHE BOOL "" FORCE) + set(DRACO_GLTF_BITSTREAM ON CACHE BOOL "" FORCE) # Disable unnecessary or omitted components set(DRACO_JS_GLUE OFF CACHE BOOL "" FORCE) set(DRACO_WASM OFF CACHE BOOL "" FORCE) @@ -705,15 +721,17 @@ ELSE() set(draco_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/contrib/draco/src") # This is probably wrong - INSTALL( TARGETS ${draco_LIBRARIES} - EXPORT "${TARGETS_EXPORT_NAME}" - LIBRARY DESTINATION ${ASSIMP_LIB_INSTALL_DIR} - ARCHIVE DESTINATION ${ASSIMP_LIB_INSTALL_DIR} - RUNTIME DESTINATION ${ASSIMP_BIN_INSTALL_DIR} - FRAMEWORK DESTINATION ${ASSIMP_LIB_INSTALL_DIR} - COMPONENT ${LIBASSIMP_COMPONENT} - INCLUDES DESTINATION include - ) + if (ASSIMP_INSTALL) + INSTALL( TARGETS ${draco_LIBRARIES} + EXPORT "${TARGETS_EXPORT_NAME}" + LIBRARY DESTINATION ${ASSIMP_LIB_INSTALL_DIR} + ARCHIVE DESTINATION ${ASSIMP_LIB_INSTALL_DIR} + RUNTIME DESTINATION ${ASSIMP_BIN_INSTALL_DIR} + FRAMEWORK DESTINATION ${ASSIMP_LIB_INSTALL_DIR} + COMPONENT ${LIBASSIMP_COMPONENT} + INCLUDES DESTINATION include + ) + endif() ENDIF() ENDIF() ENDIF() @@ -757,8 +775,8 @@ IF ( ASSIMP_INSTALL ) ENDIF() CONFIGURE_FILE( - ${CMAKE_CURRENT_LIST_DIR}/revision.h.in - ${CMAKE_CURRENT_BINARY_DIR}/revision.h + ${CMAKE_CURRENT_LIST_DIR}/include/assimp/revision.h.in + ${CMAKE_CURRENT_BINARY_DIR}/include/assimp/revision.h ) CONFIGURE_FILE( diff --git a/Dockerfile b/Dockerfile index eb5715d0f..716e8b5d8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:22.04 -RUN apt-get update && apt-get install -y ninja-build \ +RUN apt-get update && apt-get install --no-install-recommends -y ninja-build \ git cmake build-essential software-properties-common RUN add-apt-repository ppa:ubuntu-toolchain-r/test && apt-get update diff --git a/Readme.md b/Readme.md index eae0cf00a..920a309a2 100644 --- a/Readme.md +++ b/Readme.md @@ -26,12 +26,11 @@ Clone [our model database](https://github.com/assimp/assimp-mdb). ### Communities ### - Ask questions at [the Assimp Discussion Board](https://github.com/assimp/assimp/discussions). +- Find us on [https://discord.gg/s9KJfaem](https://discord.gg/kKazXMXDy2) - Ask [the Assimp community on Reddit](https://www.reddit.com/r/Assimp/). - Ask on [StackOverflow with the assimp-tag](http://stackoverflow.com/questions/tagged/assimp?sort=newest). - Nothing has worked? File a question or an issue-report at [The Assimp-Issue Tracker](https://github.com/assimp/assimp/issues) -And we also have a Gitter-channel:Gitter [![Join the chat at https://gitter.im/assimp/assimp](https://badges.gitter.im/assimp/assimp.svg)](https://gitter.im/assimp/assimp?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
- #### Supported file formats #### See [the complete list of supported formats](https://github.com/assimp/assimp/blob/master/doc/Fileformats.md). diff --git a/cmake-modules/HunterGate.cmake b/cmake-modules/HunterGate.cmake index 6d9cc2401..fe02e3614 100644 --- a/cmake-modules/HunterGate.cmake +++ b/cmake-modules/HunterGate.cmake @@ -22,38 +22,9 @@ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# This is a gate file to Hunter package manager. -# Include this file using `include` command and add package you need, example: -# -# cmake_minimum_required(VERSION 3.2) -# -# include("cmake/HunterGate.cmake") -# HunterGate( -# URL "https://github.com/path/to/hunter/archive.tar.gz" -# SHA1 "798501e983f14b28b10cda16afa4de69eee1da1d" -# ) -# -# project(MyProject) -# -# hunter_add_package(Foo) -# hunter_add_package(Boo COMPONENTS Bar Baz) -# -# Projects: -# * https://github.com/hunter-packages/gate/ -# * https://github.com/ruslo/hunter option(HUNTER_ENABLED "Enable Hunter package manager support" ON) -if(HUNTER_ENABLED) - if(CMAKE_VERSION VERSION_LESS "3.2") - message( - FATAL_ERROR - "At least CMake version 3.2 required for Hunter dependency management." - " Update CMake or set HUNTER_ENABLED to OFF." - ) - endif() -endif() - include(CMakeParseArguments) # cmake_parse_arguments option(HUNTER_STATUS_PRINT "Print working status" ON) diff --git a/code/AssetLib/3DS/3DSConverter.cpp b/code/AssetLib/3DS/3DSConverter.cpp index 7ae756668..7d1c24cd6 100644 --- a/code/AssetLib/3DS/3DSConverter.cpp +++ b/code/AssetLib/3DS/3DSConverter.cpp @@ -709,7 +709,7 @@ void Discreet3DSImporter::GenerateNodeGraph(aiScene *pcOut) { pcNode->mNumMeshes = 1; // Build a name for the node - pcNode->mName.length = ai_snprintf(pcNode->mName.data, MAXLEN, "3DSMesh_%u", i); + pcNode->mName.length = ai_snprintf(pcNode->mName.data, AI_MAXLEN, "3DSMesh_%u", i); } // Build dummy nodes for all cameras diff --git a/code/AssetLib/3DS/3DSExporter.cpp b/code/AssetLib/3DS/3DSExporter.cpp index 8477ed09f..5341a69f1 100644 --- a/code/AssetLib/3DS/3DSExporter.cpp +++ b/code/AssetLib/3DS/3DSExporter.cpp @@ -52,6 +52,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include #include diff --git a/code/AssetLib/3DS/3DSHelper.h b/code/AssetLib/3DS/3DSHelper.h index 8f85f2e4d..271a2cc7b 100644 --- a/code/AssetLib/3DS/3DSHelper.h +++ b/code/AssetLib/3DS/3DSHelper.h @@ -365,14 +365,13 @@ struct Texture { #ifdef _MSC_VER #pragma warning(pop) #endif // _MSC_VER - // --------------------------------------------------------------------------- /** Helper structure representing a 3ds material */ struct Material { //! Default constructor has been deleted Material() : mName(), - mDiffuse(ai_real(0.6), ai_real(0.6), ai_real(0.6)), + mDiffuse(0.6f, 0.6f, 0.6f), mSpecularExponent(ai_real(0.0)), mShininessStrength(ai_real(1.0)), mShading(Discreet3DS::Gouraud), @@ -385,7 +384,7 @@ struct Material { //! Constructor with explicit name explicit Material(const std::string &name) : mName(name), - mDiffuse(ai_real(0.6), ai_real(0.6), ai_real(0.6)), + mDiffuse(0.6f, 0.6f, 0.6f), mSpecularExponent(ai_real(0.0)), mShininessStrength(ai_real(1.0)), mShading(Discreet3DS::Gouraud), diff --git a/code/AssetLib/3MF/XmlSerializer.cpp b/code/AssetLib/3MF/XmlSerializer.cpp index 354ed19bb..fdc9f5a3d 100644 --- a/code/AssetLib/3MF/XmlSerializer.cpp +++ b/code/AssetLib/3MF/XmlSerializer.cpp @@ -57,8 +57,8 @@ static constexpr size_t ColRGBA_Len = 9; static constexpr size_t ColRGB_Len = 7; // format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1) -bool validateColorString(const char *color) { - const size_t len = strlen(color); +bool validateColorString(const std::string color) { + const size_t len = color.size(); if (ColRGBA_Len != len && ColRGB_Len != len) { return false; } @@ -157,8 +157,8 @@ aiMatrix4x4 parseTransformMatrix(const std::string& matrixStr) { return transformMatrix; } -bool parseColor(const char *color, aiColor4D &diffuse) { - if (nullptr == color) { +bool parseColor(const std::string &color, aiColor4D &diffuse) { + if (color.empty()) { return false; } @@ -178,7 +178,7 @@ bool parseColor(const char *color, aiColor4D &diffuse) { char b[3] = { color[5], color[6], '\0' }; diffuse.b = static_cast(strtol(b, nullptr, 16)) / ai_real(255.0); - const size_t len = strlen(color); + const size_t len = color.size(); if (ColRGB_Len == len) { return true; } diff --git a/code/AssetLib/AC/ACLoader.cpp b/code/AssetLib/AC/ACLoader.cpp index dbcb39b30..242364150 100644 --- a/code/AssetLib/AC/ACLoader.cpp +++ b/code/AssetLib/AC/ACLoader.cpp @@ -193,7 +193,7 @@ bool AC3DImporter::LoadObjectSection(std::vector &objects) { // Generate a default name for both the light source and the node // FIXME - what's the right way to print a size_t? Is 'zu' universally available? stick with the safe version. - light->mName.length = ::ai_snprintf(light->mName.data, MAXLEN, "ACLight_%i", static_cast(mLights->size()) - 1); + light->mName.length = ::ai_snprintf(light->mName.data, AI_MAXLEN, "ACLight_%i", static_cast(mLights->size()) - 1); obj.name = std::string(light->mName.data); ASSIMP_LOG_VERBOSE_DEBUG("AC3D: Light source encountered"); @@ -696,18 +696,18 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object, // generate a name depending on the type of the node switch (object.type) { case Object::Group: - node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACGroup_%i", mGroupsCounter++); + node->mName.length = ::ai_snprintf(node->mName.data, AI_MAXLEN, "ACGroup_%i", mGroupsCounter++); break; case Object::Poly: - node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACPoly_%i", mPolysCounter++); + node->mName.length = ::ai_snprintf(node->mName.data, AI_MAXLEN, "ACPoly_%i", mPolysCounter++); break; case Object::Light: - node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACLight_%i", mLightsCounter++); + node->mName.length = ::ai_snprintf(node->mName.data, AI_MAXLEN, "ACLight_%i", mLightsCounter++); break; // there shouldn't be more than one world, but we don't care case Object::World: - node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACWorld_%i", mWorldsCounter++); + node->mName.length = ::ai_snprintf(node->mName.data, AI_MAXLEN, "ACWorld_%i", mWorldsCounter++); break; } } diff --git a/code/AssetLib/AMF/AMFImporter.cpp b/code/AssetLib/AMF/AMFImporter.cpp index 7c0d3b4e9..7861c592e 100644 --- a/code/AssetLib/AMF/AMFImporter.cpp +++ b/code/AssetLib/AMF/AMFImporter.cpp @@ -178,28 +178,6 @@ bool AMFImporter::XML_SearchNode(const std::string &nodeName) { return nullptr != mXmlParser->findNode(nodeName); } -void AMFImporter::ParseHelper_FixTruncatedFloatString(const char *pInStr, std::string &pOutString) { - size_t instr_len; - - pOutString.clear(); - instr_len = strlen(pInStr); - if (!instr_len) return; - - pOutString.reserve(instr_len * 3 / 2); - // check and correct floats in format ".x". Must be "x.y". - if (pInStr[0] == '.') pOutString.push_back('0'); - - pOutString.push_back(pInStr[0]); - for (size_t ci = 1; ci < instr_len; ci++) { - if ((pInStr[ci] == '.') && ((pInStr[ci - 1] == ' ') || (pInStr[ci - 1] == '-') || (pInStr[ci - 1] == '+') || (pInStr[ci - 1] == '\t'))) { - pOutString.push_back('0'); - pOutString.push_back('.'); - } else { - pOutString.push_back(pInStr[ci]); - } - } -} - static bool ParseHelper_Decode_Base64_IsBase64(const char pChar) { return (isalnum((unsigned char)pChar) || (pChar == '+') || (pChar == '/')); } @@ -213,7 +191,10 @@ void AMFImporter::ParseHelper_Decode_Base64(const std::string &pInputBase64, std uint8_t arr4[4], arr3[3]; // check input data - if (pInputBase64.size() % 4) throw DeadlyImportError("Base64-encoded data must have size multiply of four."); + if (pInputBase64.size() % 4) { + throw DeadlyImportError("Base64-encoded data must have size multiply of four."); + } + // prepare output place pOutputData.clear(); pOutputData.reserve(pInputBase64.size() / 4 * 3); @@ -403,17 +384,17 @@ void AMFImporter::ParseNode_Instance(XmlNode &node) { for (auto ¤tNode : node.children()) { const std::string ¤tName = currentNode.name(); if (currentName == "deltax") { - XmlParser::getValueAsFloat(currentNode, als.Delta.x); + XmlParser::getValueAsReal(currentNode, als.Delta.x); } else if (currentName == "deltay") { - XmlParser::getValueAsFloat(currentNode, als.Delta.y); + XmlParser::getValueAsReal(currentNode, als.Delta.y); } else if (currentName == "deltaz") { - XmlParser::getValueAsFloat(currentNode, als.Delta.z); + XmlParser::getValueAsReal(currentNode, als.Delta.z); } else if (currentName == "rx") { - XmlParser::getValueAsFloat(currentNode, als.Delta.x); + XmlParser::getValueAsReal(currentNode, als.Delta.x); } else if (currentName == "ry") { - XmlParser::getValueAsFloat(currentNode, als.Delta.y); + XmlParser::getValueAsReal(currentNode, als.Delta.y); } else if (currentName == "rz") { - XmlParser::getValueAsFloat(currentNode, als.Delta.z); + XmlParser::getValueAsReal(currentNode, als.Delta.z); } } ParseHelper_Node_Exit(); diff --git a/code/AssetLib/AMF/AMFImporter.hpp b/code/AssetLib/AMF/AMFImporter.hpp index 50be465ce..97e0a7118 100644 --- a/code/AssetLib/AMF/AMFImporter.hpp +++ b/code/AssetLib/AMF/AMFImporter.hpp @@ -168,7 +168,6 @@ public: AI_WONT_RETURN void Throw_ID_NotFound(const std::string &pID) const AI_WONT_RETURN_SUFFIX; void XML_CheckNode_MustHaveChildren(pugi::xml_node &node); bool XML_SearchNode(const std::string &nodeName); - void ParseHelper_FixTruncatedFloatString(const char *pInStr, std::string &pOutString); AMFImporter(const AMFImporter &pScene) = delete; AMFImporter &operator=(const AMFImporter &pScene) = delete; diff --git a/code/AssetLib/AMF/AMFImporter_Geometry.cpp b/code/AssetLib/AMF/AMFImporter_Geometry.cpp index db262dfbd..b1d87eb2e 100644 --- a/code/AssetLib/AMF/AMFImporter_Geometry.cpp +++ b/code/AssetLib/AMF/AMFImporter_Geometry.cpp @@ -167,11 +167,11 @@ void AMFImporter::ParseNode_Coordinates(XmlNode &node) { AMFCoordinates &als = *((AMFCoordinates *)ne); // alias for convenience const std::string ¤tName = ai_tolower(currentNode.name()); if (currentName == "x") { - XmlParser::getValueAsFloat(currentNode, als.Coordinate.x); + XmlParser::getValueAsReal(currentNode, als.Coordinate.x); } else if (currentName == "y") { - XmlParser::getValueAsFloat(currentNode, als.Coordinate.y); + XmlParser::getValueAsReal(currentNode, als.Coordinate.y); } else if (currentName == "z") { - XmlParser::getValueAsFloat(currentNode, als.Coordinate.z); + XmlParser::getValueAsReal(currentNode, als.Coordinate.z); } } ParseHelper_Node_Exit(); diff --git a/code/AssetLib/AMF/AMFImporter_Material.cpp b/code/AssetLib/AMF/AMFImporter_Material.cpp index ae27f5d37..74fe2a1b6 100644 --- a/code/AssetLib/AMF/AMFImporter_Material.cpp +++ b/code/AssetLib/AMF/AMFImporter_Material.cpp @@ -263,26 +263,25 @@ void AMFImporter::ParseNode_TexMap(XmlNode &node, const bool pUseOldName) { const std::string &name = currentNode.name(); if (name == "utex1") { read_flag[0] = true; - XmlParser::getValueAsFloat(node, als.TextureCoordinate[0].x); + XmlParser::getValueAsReal(node, als.TextureCoordinate[0].x); } else if (name == "utex2") { read_flag[1] = true; - XmlParser::getValueAsFloat(node, als.TextureCoordinate[1].x); + XmlParser::getValueAsReal(node, als.TextureCoordinate[1].x); } else if (name == "utex3") { read_flag[2] = true; - XmlParser::getValueAsFloat(node, als.TextureCoordinate[2].x); + XmlParser::getValueAsReal(node, als.TextureCoordinate[2].x); } else if (name == "vtex1") { read_flag[3] = true; - XmlParser::getValueAsFloat(node, als.TextureCoordinate[0].y); + XmlParser::getValueAsReal(node, als.TextureCoordinate[0].y); } else if (name == "vtex2") { read_flag[4] = true; - XmlParser::getValueAsFloat(node, als.TextureCoordinate[1].y); + XmlParser::getValueAsReal(node, als.TextureCoordinate[1].y); } else if (name == "vtex3") { read_flag[5] = true; - XmlParser::getValueAsFloat(node, als.TextureCoordinate[2].y); + XmlParser::getValueAsReal(node, als.TextureCoordinate[2].y); } } ParseHelper_Node_Exit(); - } else { for (pugi::xml_attribute &attr : node.attributes()) { const std::string name = attr.name(); diff --git a/code/AssetLib/AMF/AMFImporter_Node.hpp b/code/AssetLib/AMF/AMFImporter_Node.hpp index 21068a9ba..2b4f6717d 100644 --- a/code/AssetLib/AMF/AMFImporter_Node.hpp +++ b/code/AssetLib/AMF/AMFImporter_Node.hpp @@ -56,7 +56,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -/// \class CAMFImporter_NodeElement /// Base class for elements of nodes. class AMFNodeElementBase { public: @@ -106,7 +105,6 @@ protected: } }; // class IAMFImporter_NodeElement -/// \struct CAMFImporter_NodeElement_Constellation /// A collection of objects or constellations with specific relative locations. struct AMFConstellation : public AMFNodeElementBase { /// Constructor. @@ -116,7 +114,6 @@ struct AMFConstellation : public AMFNodeElementBase { }; // struct CAMFImporter_NodeElement_Constellation -/// \struct CAMFImporter_NodeElement_Instance /// Part of constellation. struct AMFInstance : public AMFNodeElementBase { @@ -135,7 +132,6 @@ struct AMFInstance : public AMFNodeElementBase { AMFNodeElementBase(ENET_Instance, pParent) {} }; -/// \struct CAMFImporter_NodeElement_Metadata /// Structure that define metadata node. struct AMFMetadata : public AMFNodeElementBase { @@ -148,7 +144,6 @@ struct AMFMetadata : public AMFNodeElementBase { AMFNodeElementBase(ENET_Metadata, pParent) {} }; -/// \struct CAMFImporter_NodeElement_Root /// Structure that define root node. struct AMFRoot : public AMFNodeElementBase { @@ -161,7 +156,6 @@ struct AMFRoot : public AMFNodeElementBase { AMFNodeElementBase(ENET_Root, pParent) {} }; -/// \struct CAMFImporter_NodeElement_Color /// Structure that define object node. struct AMFColor : public AMFNodeElementBase { bool Composed; ///< Type of color stored: if true then look for formula in \ref Color_Composed[4], else - in \ref Color. @@ -177,7 +171,6 @@ struct AMFColor : public AMFNodeElementBase { } }; -/// \struct CAMFImporter_NodeElement_Material /// Structure that define material node. struct AMFMaterial : public AMFNodeElementBase { @@ -187,7 +180,6 @@ struct AMFMaterial : public AMFNodeElementBase { AMFNodeElementBase(ENET_Material, pParent) {} }; -/// \struct CAMFImporter_NodeElement_Object /// Structure that define object node. struct AMFObject : public AMFNodeElementBase { @@ -206,7 +198,6 @@ struct AMFMesh : public AMFNodeElementBase { AMFNodeElementBase(ENET_Mesh, pParent) {} }; -/// \struct CAMFImporter_NodeElement_Vertex /// Structure that define vertex node. struct AMFVertex : public AMFNodeElementBase { /// Constructor. @@ -215,7 +206,6 @@ struct AMFVertex : public AMFNodeElementBase { AMFNodeElementBase(ENET_Vertex, pParent) {} }; -/// \struct CAMFImporter_NodeElement_Edge /// Structure that define edge node. struct AMFEdge : public AMFNodeElementBase { /// Constructor. @@ -224,7 +214,6 @@ struct AMFEdge : public AMFNodeElementBase { AMFNodeElementBase(ENET_Edge, pParent) {} }; -/// \struct CAMFImporter_NodeElement_Vertices /// Structure that define vertices node. struct AMFVertices : public AMFNodeElementBase { /// Constructor. @@ -233,7 +222,6 @@ struct AMFVertices : public AMFNodeElementBase { AMFNodeElementBase(ENET_Vertices, pParent) {} }; -/// \struct CAMFImporter_NodeElement_Volume /// Structure that define volume node. struct AMFVolume : public AMFNodeElementBase { std::string MaterialID; ///< Which material to use. @@ -245,7 +233,6 @@ struct AMFVolume : public AMFNodeElementBase { AMFNodeElementBase(ENET_Volume, pParent) {} }; -/// \struct CAMFImporter_NodeElement_Coordinates /// Structure that define coordinates node. struct AMFCoordinates : public AMFNodeElementBase { aiVector3D Coordinate; ///< Coordinate. @@ -256,7 +243,6 @@ struct AMFCoordinates : public AMFNodeElementBase { AMFNodeElementBase(ENET_Coordinates, pParent) {} }; -/// \struct CAMFImporter_NodeElement_TexMap /// Structure that define texture coordinates node. struct AMFTexMap : public AMFNodeElementBase { aiVector3D TextureCoordinate[3]; ///< Texture coordinates. @@ -273,7 +259,6 @@ struct AMFTexMap : public AMFNodeElementBase { } }; -/// \struct CAMFImporter_NodeElement_Triangle /// Structure that define triangle node. struct AMFTriangle : public AMFNodeElementBase { size_t V[3]; ///< Triangle vertices. diff --git a/code/AssetLib/AMF/AMFImporter_Postprocess.cpp b/code/AssetLib/AMF/AMFImporter_Postprocess.cpp index 969c64bd2..bc6fb42a8 100644 --- a/code/AssetLib/AMF/AMFImporter_Postprocess.cpp +++ b/code/AssetLib/AMF/AMFImporter_Postprocess.cpp @@ -224,7 +224,8 @@ size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string & } // Create format hint. - strcpy(converted_texture.FormatHint, "rgba0000"); // copy initial string. + constexpr char templateColor[] = "rgba0000"; + memcpy(converted_texture.FormatHint, templateColor, 8); if (!r.empty()) converted_texture.FormatHint[4] = '8'; if (!g.empty()) converted_texture.FormatHint[5] = '8'; if (!b.empty()) converted_texture.FormatHint[6] = '8'; @@ -867,7 +868,7 @@ nl_clean_loop: pScene->mTextures[idx]->mHeight = static_cast(tex_convd.Height); pScene->mTextures[idx]->pcData = (aiTexel *)tex_convd.Data; // texture format description. - strcpy(pScene->mTextures[idx]->achFormatHint, tex_convd.FormatHint); + strncpy(pScene->mTextures[idx]->achFormatHint, tex_convd.FormatHint, HINTMAXTEXTURELEN); idx++; } // for(const SPP_Texture& tex_convd: mTexture_Converted) diff --git a/code/AssetLib/ASE/ASELoader.cpp b/code/AssetLib/ASE/ASELoader.cpp index 7e411fc03..c5f2eba32 100644 --- a/code/AssetLib/ASE/ASELoader.cpp +++ b/code/AssetLib/ASE/ASELoader.cpp @@ -124,7 +124,7 @@ void ASEImporter::InternReadFile(const std::string &pFile, // Allocate storage and copy the contents of the file to a memory buffer std::vector mBuffer2; TextFileToBuffer(file.get(), mBuffer2); - + const size_t fileSize = mBuffer2.size(); this->mBuffer = &mBuffer2[0]; this->pcScene = pScene; @@ -146,7 +146,7 @@ void ASEImporter::InternReadFile(const std::string &pFile, }; // Construct an ASE parser and parse the file - ASE::Parser parser(mBuffer, defaultFormat); + ASE::Parser parser(mBuffer, fileSize, defaultFormat); mParser = &parser; mParser->Parse(); @@ -446,10 +446,9 @@ void ASEImporter::BuildLights() { } // ------------------------------------------------------------------------------------------------ -void ASEImporter::AddNodes(const std::vector &nodes, - aiNode *pcParent, const char *szName) { +void ASEImporter::AddNodes(const std::vector &nodes, aiNode *pcParent, const std::string &name) { aiMatrix4x4 m; - AddNodes(nodes, pcParent, szName, m); + AddNodes(nodes, pcParent, name, m); } // ------------------------------------------------------------------------------------------------ @@ -506,10 +505,9 @@ void ASEImporter::AddMeshes(const ASE::BaseNode *snode, aiNode *node) { // ------------------------------------------------------------------------------------------------ // Add child nodes to a given parent node -void ASEImporter::AddNodes(const std::vector &nodes, - aiNode *pcParent, const char *szName, +void ASEImporter::AddNodes(const std::vector &nodes, aiNode *pcParent, const std::string &name, const aiMatrix4x4 &mat) { - const size_t len = szName ? ::strlen(szName) : 0; + const size_t len = name.size(); ai_assert(4 <= AI_MAX_NUMBER_OF_COLOR_SETS); // Receives child nodes for the pcParent node @@ -519,16 +517,18 @@ void ASEImporter::AddNodes(const std::vector &nodes, // which has *us* as parent. for (std::vector::const_iterator it = nodes.begin(), end = nodes.end(); it != end; ++it) { const BaseNode *snode = *it; - if (szName) { - if (len != snode->mParent.length() || ::strcmp(szName, snode->mParent.c_str())) + if (!name.empty()) { + if (len != snode->mParent.length() || name != snode->mParent.c_str()) { continue; - } else if (snode->mParent.length()) + } + } else if (snode->mParent.length()) { continue; + } (*it)->mProcessed = true; // Allocate a new node and add it to the output data structure - apcNodes.push_back(new aiNode()); + apcNodes.push_back(new aiNode); aiNode *node = apcNodes.back(); node->mName.Set((snode->mName.length() ? snode->mName.c_str() : "Unnamed_Node")); @@ -541,7 +541,7 @@ void ASEImporter::AddNodes(const std::vector &nodes, // Add sub nodes - prevent stack overflow due to recursive parenting if (node->mName != node->mParent->mName && node->mName != node->mParent->mParent->mName) { - AddNodes(nodes, node, node->mName.data, snode->mTransform); + AddNodes(nodes, node, node->mName.C_Str(), snode->mTransform); } // Further processing depends on the type of the node @@ -619,7 +619,8 @@ void ASEImporter::BuildNodes(std::vector &nodes) { } // add all nodes - AddNodes(nodes, ch, nullptr); + static const std::string none = ""; + AddNodes(nodes, ch, none); // now iterate through al nodes and find those that have not yet // been added to the nodegraph (= their parent could not be recognized) diff --git a/code/AssetLib/ASE/ASELoader.h b/code/AssetLib/ASE/ASELoader.h index 5654fa630..99d5119ed 100644 --- a/code/AssetLib/ASE/ASELoader.h +++ b/code/AssetLib/ASE/ASELoader.h @@ -153,13 +153,13 @@ private: * \param matrix Current transform */ void AddNodes(const std::vector& nodes, - aiNode* pcParent,const char* szName); + aiNode* pcParent, const std::string &name); void AddNodes(const std::vector& nodes, - aiNode* pcParent,const char* szName, + aiNode* pcParent, const std::string &name, const aiMatrix4x4& matrix); - void AddMeshes(const ASE::BaseNode* snode,aiNode* node); + void AddMeshes(const ASE::BaseNode* snode, aiNode* node); // ------------------------------------------------------------------- /** Generate a default material and add it to the parser's list @@ -188,5 +188,4 @@ protected: } // end of namespace Assimp - #endif // AI_3DSIMPORTER_H_INC diff --git a/code/AssetLib/ASE/ASEParser.cpp b/code/AssetLib/ASE/ASEParser.cpp index e3c358aa5..c9bbe3ca6 100644 --- a/code/AssetLib/ASE/ASEParser.cpp +++ b/code/AssetLib/ASE/ASEParser.cpp @@ -66,23 +66,24 @@ using namespace Assimp::ASE; // Handle a "top-level" section in the file. EOF is no error in this case. #define AI_ASE_HANDLE_TOP_LEVEL_SECTION() \ - else if ('{' == *filePtr) iDepth++; \ - else if ('}' == *filePtr) { \ + else if ('{' == *mFilePtr) \ + ++iDepth; \ + else if ('}' == *mFilePtr) { \ if (0 == --iDepth) { \ - ++filePtr; \ + ++mFilePtr; \ SkipToNextToken(); \ return; \ } \ } \ - if ('\0' == *filePtr) { \ + if ('\0' == *mFilePtr) { \ return; \ } \ - if (IsLineEnd(*filePtr) && !bLastWasEndLine) { \ + if (IsLineEnd(*mFilePtr) && !bLastWasEndLine) {\ ++iLineNumber; \ bLastWasEndLine = true; \ } else \ bLastWasEndLine = false; \ - ++filePtr; + ++mFilePtr; // ------------------------------------------------------------------------------------------------ // Handle a nested section in the file. EOF is an error in this case @@ -90,32 +91,32 @@ using namespace Assimp::ASE; // @param msg Full name of the section (including the asterisk) #define AI_ASE_HANDLE_SECTION(level, msg) \ - if ('{' == *filePtr) \ + if ('{' == *mFilePtr) \ iDepth++; \ - else if ('}' == *filePtr) { \ + else if ('}' == *mFilePtr) { \ if (0 == --iDepth) { \ - ++filePtr; \ + ++mFilePtr; \ SkipToNextToken(); \ return; \ } \ - } else if ('\0' == *filePtr) { \ + } else if ('\0' == *mFilePtr) { \ LogError("Encountered unexpected EOL while parsing a " msg \ " chunk (Level " level ")"); \ } \ - if (IsLineEnd(*filePtr) && !bLastWasEndLine) { \ + if (IsLineEnd(*mFilePtr) && !bLastWasEndLine) { \ ++iLineNumber; \ bLastWasEndLine = true; \ } else \ bLastWasEndLine = false; \ - ++filePtr; + ++mFilePtr; // ------------------------------------------------------------------------------------------------ -Parser::Parser(const char *szFile, unsigned int fileFormatDefault) : - filePtr(nullptr), mEnd (nullptr) { - ai_assert(nullptr != szFile); +Parser::Parser(const char *file, size_t fileLen, unsigned int fileFormatDefault) : + mFilePtr(nullptr), mEnd (nullptr) { + ai_assert(file != nullptr); - filePtr = szFile; - mEnd = filePtr + std::strlen(filePtr); + mFilePtr = file; + mEnd = mFilePtr + fileLen; iFileFormat = fileFormatDefault; // make sure that the color values are invalid @@ -179,9 +180,9 @@ AI_WONT_RETURN void Parser::LogError(const char *szWarn) { // ------------------------------------------------------------------------------------------------ bool Parser::SkipToNextToken() { while (true) { - char me = *filePtr; + char me = *mFilePtr; - if (filePtr == mEnd) { + if (mFilePtr == mEnd) { return false; } @@ -198,7 +199,7 @@ bool Parser::SkipToNextToken() { return false; } - ++filePtr; + ++mFilePtr; } } @@ -207,22 +208,22 @@ bool Parser::SkipSection() { // must handle subsections ... int iCnt = 0; while (true) { - if ('}' == *filePtr) { + if ('}' == *mFilePtr) { --iCnt; if (0 == iCnt) { // go to the next valid token ... - ++filePtr; + ++mFilePtr; SkipToNextToken(); return true; } - } else if ('{' == *filePtr) { + } else if ('{' == *mFilePtr) { ++iCnt; - } else if ('\0' == *filePtr) { + } else if ('\0' == *mFilePtr) { LogWarning("Unable to parse block: Unexpected EOF, closing bracket \'}\' was expected [#1]"); return false; - } else if (IsLineEnd(*filePtr)) + } else if (IsLineEnd(*mFilePtr)) ++iLineNumber; - ++filePtr; + ++mFilePtr; } } @@ -230,11 +231,11 @@ bool Parser::SkipSection() { void Parser::Parse() { AI_ASE_PARSER_INIT(); while (true) { - if ('*' == *filePtr) { - ++filePtr; + if ('*' == *mFilePtr) { + ++mFilePtr; // Version should be 200. Validate this ... - if (TokenMatch(filePtr, "3DSMAX_ASCIIEXPORT", 18)) { + if (TokenMatch(mFilePtr, "3DSMAX_ASCIIEXPORT", 18)) { unsigned int fmt; ParseLV4MeshLong(fmt); @@ -255,23 +256,23 @@ void Parser::Parse() { continue; } // main scene information - if (TokenMatch(filePtr, "SCENE", 5)) { + if (TokenMatch(mFilePtr, "SCENE", 5)) { ParseLV1SceneBlock(); continue; } // "group" - no implementation yet, in facte // we're just ignoring them for the moment - if (TokenMatch(filePtr, "GROUP", 5)) { + if (TokenMatch(mFilePtr, "GROUP", 5)) { Parse(); continue; } // material list - if (TokenMatch(filePtr, "MATERIAL_LIST", 13)) { + if (TokenMatch(mFilePtr, "MATERIAL_LIST", 13)) { ParseLV1MaterialListBlock(); continue; } // geometric object (mesh) - if (TokenMatch(filePtr, "GEOMOBJECT", 10)) + if (TokenMatch(mFilePtr, "GEOMOBJECT", 10)) { m_vMeshes.emplace_back("UNNAMED"); @@ -279,7 +280,7 @@ void Parser::Parse() { continue; } // helper object = dummy in the hierarchy - if (TokenMatch(filePtr, "HELPEROBJECT", 12)) + if (TokenMatch(mFilePtr, "HELPEROBJECT", 12)) { m_vDummies.emplace_back(); @@ -287,7 +288,7 @@ void Parser::Parse() { continue; } // light object - if (TokenMatch(filePtr, "LIGHTOBJECT", 11)) + if (TokenMatch(mFilePtr, "LIGHTOBJECT", 11)) { m_vLights.emplace_back("UNNAMED"); @@ -295,20 +296,20 @@ void Parser::Parse() { continue; } // camera object - if (TokenMatch(filePtr, "CAMERAOBJECT", 12)) { + if (TokenMatch(mFilePtr, "CAMERAOBJECT", 12)) { m_vCameras.emplace_back("UNNAMED"); ParseLV1ObjectBlock(m_vCameras.back()); continue; } // comment - print it on the console - if (TokenMatch(filePtr, "COMMENT", 7)) { + if (TokenMatch(mFilePtr, "COMMENT", 7)) { std::string out = ""; ParseString(out, "*COMMENT"); LogInfo(("Comment: " + out).c_str()); continue; } // ASC bone weights - if (AI_ASE_IS_OLD_FILE_FORMAT() && TokenMatch(filePtr, "MESH_SOFTSKINVERTS", 18)) { + if (AI_ASE_IS_OLD_FILE_FORMAT() && TokenMatch(mFilePtr, "MESH_SOFTSKINVERTS", 18)) { ParseLV1SoftSkinBlock(); } } @@ -340,25 +341,25 @@ void Parser::ParseLV1SoftSkinBlock() { */ // ************************************************************** while (true) { - if (*filePtr == '}') { - ++filePtr; + if (*mFilePtr == '}') { + ++mFilePtr; return; - } else if (*filePtr == '\0') + } else if (*mFilePtr == '\0') return; - else if (*filePtr == '{') - ++filePtr; + else if (*mFilePtr == '{') + ++mFilePtr; else // if (!IsSpace(*filePtr) && !IsLineEnd(*filePtr)) { ASE::Mesh *curMesh = nullptr; unsigned int numVerts = 0; - const char *sz = filePtr; - while (!IsSpaceOrNewLine(*filePtr)) { - ++filePtr; + const char *sz = mFilePtr; + while (!IsSpaceOrNewLine(*mFilePtr)) { + ++mFilePtr; } - const unsigned int diff = (unsigned int)(filePtr - sz); + const unsigned int diff = (unsigned int)(mFilePtr - sz); if (diff) { std::string name = std::string(sz, diff); for (std::vector::iterator it = m_vMeshes.begin(); @@ -374,24 +375,24 @@ void Parser::ParseLV1SoftSkinBlock() { // Skip the mesh data - until we find a new mesh // or the end of the *MESH_SOFTSKINVERTS section while (true) { - SkipSpacesAndLineEnd(&filePtr, mEnd); - if (*filePtr == '}') { - ++filePtr; + SkipSpacesAndLineEnd(&mFilePtr, mEnd); + if (*mFilePtr == '}') { + ++mFilePtr; return; - } else if (!IsNumeric(*filePtr)) + } else if (!IsNumeric(*mFilePtr)) break; - SkipLine(&filePtr, mEnd); + SkipLine(&mFilePtr, mEnd); } } else { - SkipSpacesAndLineEnd(&filePtr, mEnd); + SkipSpacesAndLineEnd(&mFilePtr, mEnd); ParseLV4MeshLong(numVerts); // Reserve enough storage curMesh->mBoneVertices.reserve(numVerts); for (unsigned int i = 0; i < numVerts; ++i) { - SkipSpacesAndLineEnd(&filePtr, mEnd); + SkipSpacesAndLineEnd(&mFilePtr, mEnd); unsigned int numWeights; ParseLV4MeshLong(numWeights); @@ -421,7 +422,7 @@ void Parser::ParseLV1SoftSkinBlock() { me.first = static_cast(curMesh->mBones.size()); curMesh->mBones.emplace_back(bone); } - ParseLV4MeshFloat(me.second); + ParseLV4MeshReal(me.second); // Add the new bone weight to list vert.mBoneWeights.push_back(me); @@ -430,10 +431,10 @@ void Parser::ParseLV1SoftSkinBlock() { } } } - if (*filePtr == '\0') + if (*mFilePtr == '\0') return; - ++filePtr; - SkipSpacesAndLineEnd(&filePtr, mEnd); + ++mFilePtr; + SkipSpacesAndLineEnd(&mFilePtr, mEnd); } } @@ -441,35 +442,35 @@ void Parser::ParseLV1SoftSkinBlock() { void Parser::ParseLV1SceneBlock() { AI_ASE_PARSER_INIT(); while (true) { - if ('*' == *filePtr) { - ++filePtr; - if (TokenMatch(filePtr, "SCENE_BACKGROUND_STATIC", 23)) + if ('*' == *mFilePtr) { + ++mFilePtr; + if (TokenMatch(mFilePtr, "SCENE_BACKGROUND_STATIC", 23)) { // parse a color triple and assume it is really the bg color ParseLV4MeshFloatTriple(&m_clrBackground.r); continue; } - if (TokenMatch(filePtr, "SCENE_AMBIENT_STATIC", 20)) + if (TokenMatch(mFilePtr, "SCENE_AMBIENT_STATIC", 20)) { // parse a color triple and assume it is really the bg color ParseLV4MeshFloatTriple(&m_clrAmbient.r); continue; } - if (TokenMatch(filePtr, "SCENE_FIRSTFRAME", 16)) { + if (TokenMatch(mFilePtr, "SCENE_FIRSTFRAME", 16)) { ParseLV4MeshLong(iFirstFrame); continue; } - if (TokenMatch(filePtr, "SCENE_LASTFRAME", 15)) { + if (TokenMatch(mFilePtr, "SCENE_LASTFRAME", 15)) { ParseLV4MeshLong(iLastFrame); continue; } - if (TokenMatch(filePtr, "SCENE_FRAMESPEED", 16)) { + if (TokenMatch(mFilePtr, "SCENE_FRAMESPEED", 16)) { ParseLV4MeshLong(iFrameSpeed); continue; } - if (TokenMatch(filePtr, "SCENE_TICKSPERFRAME", 19)) { + if (TokenMatch(mFilePtr, "SCENE_TICKSPERFRAME", 19)) { ParseLV4MeshLong(iTicksPerFrame); continue; } @@ -485,9 +486,9 @@ void Parser::ParseLV1MaterialListBlock() { unsigned int iMaterialCount = 0; unsigned int iOldMaterialCount = (unsigned int)m_vMaterials.size(); while (true) { - if ('*' == *filePtr) { - ++filePtr; - if (TokenMatch(filePtr, "MATERIAL_COUNT", 14)) { + if ('*' == *mFilePtr) { + ++mFilePtr; + if (TokenMatch(mFilePtr, "MATERIAL_COUNT", 14)) { ParseLV4MeshLong(iMaterialCount); if (UINT_MAX - iOldMaterialCount < iMaterialCount) { @@ -499,14 +500,20 @@ void Parser::ParseLV1MaterialListBlock() { m_vMaterials.resize(iOldMaterialCount + iMaterialCount, Material("INVALID")); continue; } - if (TokenMatch(filePtr, "MATERIAL", 8)) { + if (TokenMatch(mFilePtr, "MATERIAL", 8)) { + // ensure we have at least one material allocated + if (iMaterialCount == 0) { + LogWarning("*MATERIAL_COUNT unspecified or 0"); + iMaterialCount = 1; + m_vMaterials.resize(iOldMaterialCount + iMaterialCount, Material("INVALID")); + } + unsigned int iIndex = 0; ParseLV4MeshLong(iIndex); if (iIndex >= iMaterialCount) { LogWarning("Out of range: material index is too large"); iIndex = iMaterialCount - 1; - return; } // get a reference to the material @@ -518,7 +525,7 @@ void Parser::ParseLV1MaterialListBlock() { if( iDepth == 1 ){ // CRUDE HACK: support missing brace after "Ascii Scene Exporter v2.51" LogWarning("Missing closing brace in material list"); - --filePtr; + --mFilePtr; return; } } @@ -532,37 +539,37 @@ void Parser::ParseLV2MaterialBlock(ASE::Material &mat) { unsigned int iNumSubMaterials = 0; while (true) { - if ('*' == *filePtr) { - ++filePtr; - if (TokenMatch(filePtr, "MATERIAL_NAME", 13)) { + if ('*' == *mFilePtr) { + ++mFilePtr; + if (TokenMatch(mFilePtr, "MATERIAL_NAME", 13)) { if (!ParseString(mat.mName, "*MATERIAL_NAME")) SkipToNextToken(); continue; } // ambient material color - if (TokenMatch(filePtr, "MATERIAL_AMBIENT", 16)) { + if (TokenMatch(mFilePtr, "MATERIAL_AMBIENT", 16)) { ParseLV4MeshFloatTriple(&mat.mAmbient.r); continue; } // diffuse material color - if (TokenMatch(filePtr, "MATERIAL_DIFFUSE", 16)) { + if (TokenMatch(mFilePtr, "MATERIAL_DIFFUSE", 16)) { ParseLV4MeshFloatTriple(&mat.mDiffuse.r); continue; } // specular material color - if (TokenMatch(filePtr, "MATERIAL_SPECULAR", 17)) { + if (TokenMatch(mFilePtr, "MATERIAL_SPECULAR", 17)) { ParseLV4MeshFloatTriple(&mat.mSpecular.r); continue; } // material shading type - if (TokenMatch(filePtr, "MATERIAL_SHADING", 16)) { - if (TokenMatch(filePtr, "Blinn", 5)) { + if (TokenMatch(mFilePtr, "MATERIAL_SHADING", 16)) { + if (TokenMatch(mFilePtr, "Blinn", 5)) { mat.mShading = Discreet3DS::Blinn; - } else if (TokenMatch(filePtr, "Phong", 5)) { + } else if (TokenMatch(mFilePtr, "Phong", 5)) { mat.mShading = Discreet3DS::Phong; - } else if (TokenMatch(filePtr, "Flat", 4)) { + } else if (TokenMatch(mFilePtr, "Flat", 4)) { mat.mShading = Discreet3DS::Flat; - } else if (TokenMatch(filePtr, "Wire", 4)) { + } else if (TokenMatch(mFilePtr, "Wire", 4)) { mat.mShading = Discreet3DS::Wire; } else { // assume gouraud shading @@ -572,15 +579,15 @@ void Parser::ParseLV2MaterialBlock(ASE::Material &mat) { continue; } // material transparency - if (TokenMatch(filePtr, "MATERIAL_TRANSPARENCY", 21)) { - ParseLV4MeshFloat(mat.mTransparency); + if (TokenMatch(mFilePtr, "MATERIAL_TRANSPARENCY", 21)) { + ParseLV4MeshReal(mat.mTransparency); mat.mTransparency = ai_real(1.0) - mat.mTransparency; continue; } // material self illumination - if (TokenMatch(filePtr, "MATERIAL_SELFILLUM", 18)) { + if (TokenMatch(mFilePtr, "MATERIAL_SELFILLUM", 18)) { ai_real f = 0.0; - ParseLV4MeshFloat(f); + ParseLV4MeshReal(f); mat.mEmissive.r = f; mat.mEmissive.g = f; @@ -588,71 +595,77 @@ void Parser::ParseLV2MaterialBlock(ASE::Material &mat) { continue; } // material shininess - if (TokenMatch(filePtr, "MATERIAL_SHINE", 14)) { - ParseLV4MeshFloat(mat.mSpecularExponent); + if (TokenMatch(mFilePtr, "MATERIAL_SHINE", 14)) { + ParseLV4MeshReal(mat.mSpecularExponent); mat.mSpecularExponent *= 15; continue; } // two-sided material - if (TokenMatch(filePtr, "MATERIAL_TWOSIDED", 17)) { + if (TokenMatch(mFilePtr, "MATERIAL_TWOSIDED", 17)) { mat.mTwoSided = true; continue; } // material shininess strength - if (TokenMatch(filePtr, "MATERIAL_SHINESTRENGTH", 22)) { - ParseLV4MeshFloat(mat.mShininessStrength); + if (TokenMatch(mFilePtr, "MATERIAL_SHINESTRENGTH", 22)) { + ParseLV4MeshReal(mat.mShininessStrength); continue; } // diffuse color map - if (TokenMatch(filePtr, "MAP_DIFFUSE", 11)) { + if (TokenMatch(mFilePtr, "MAP_DIFFUSE", 11)) { // parse the texture block ParseLV3MapBlock(mat.sTexDiffuse); continue; } // ambient color map - if (TokenMatch(filePtr, "MAP_AMBIENT", 11)) { + if (TokenMatch(mFilePtr, "MAP_AMBIENT", 11)) { // parse the texture block ParseLV3MapBlock(mat.sTexAmbient); continue; } // specular color map - if (TokenMatch(filePtr, "MAP_SPECULAR", 12)) { + if (TokenMatch(mFilePtr, "MAP_SPECULAR", 12)) { // parse the texture block ParseLV3MapBlock(mat.sTexSpecular); continue; } // opacity map - if (TokenMatch(filePtr, "MAP_OPACITY", 11)) { + if (TokenMatch(mFilePtr, "MAP_OPACITY", 11)) { // parse the texture block ParseLV3MapBlock(mat.sTexOpacity); continue; } // emissive map - if (TokenMatch(filePtr, "MAP_SELFILLUM", 13)) { + if (TokenMatch(mFilePtr, "MAP_SELFILLUM", 13)) { // parse the texture block ParseLV3MapBlock(mat.sTexEmissive); continue; } // bump map - if (TokenMatch(filePtr, "MAP_BUMP", 8)) { + if (TokenMatch(mFilePtr, "MAP_BUMP", 8)) { // parse the texture block ParseLV3MapBlock(mat.sTexBump); } // specular/shininess map - if (TokenMatch(filePtr, "MAP_SHINESTRENGTH", 17)) { + if (TokenMatch(mFilePtr, "MAP_SHINESTRENGTH", 17)) { // parse the texture block ParseLV3MapBlock(mat.sTexShininess); continue; } // number of submaterials - if (TokenMatch(filePtr, "NUMSUBMTLS", 10)) { + if (TokenMatch(mFilePtr, "NUMSUBMTLS", 10)) { ParseLV4MeshLong(iNumSubMaterials); // allocate enough storage mat.avSubMaterials.resize(iNumSubMaterials, Material("INVALID SUBMATERIAL")); } // submaterial chunks - if (TokenMatch(filePtr, "SUBMATERIAL", 11)) { + if (TokenMatch(mFilePtr, "SUBMATERIAL", 11)) { + // ensure we have at least one material allocated + if (iNumSubMaterials == 0) { + LogWarning("*NUMSUBMTLS unspecified or 0"); + iNumSubMaterials = 1; + mat.avSubMaterials.resize(iNumSubMaterials, Material("INVALID SUBMATERIAL")); + } unsigned int iIndex = 0; ParseLV4MeshLong(iIndex); @@ -689,10 +702,10 @@ void Parser::ParseLV3MapBlock(Texture &map) { bool parsePath = true; std::string temp; while (true) { - if ('*' == *filePtr) { - ++filePtr; + if ('*' == *mFilePtr) { + ++mFilePtr; // type of map - if (TokenMatch(filePtr, "MAP_CLASS", 9)) { + if (TokenMatch(mFilePtr, "MAP_CLASS", 9)) { temp.clear(); if (!ParseString(temp, "*MAP_CLASS")) SkipToNextToken(); @@ -703,7 +716,7 @@ void Parser::ParseLV3MapBlock(Texture &map) { continue; } // path to the texture - if (parsePath && TokenMatch(filePtr, "BITMAP", 6)) { + if (parsePath && TokenMatch(mFilePtr, "BITMAP", 6)) { if (!ParseString(map.mMapName, "*BITMAP")) SkipToNextToken(); @@ -717,33 +730,33 @@ void Parser::ParseLV3MapBlock(Texture &map) { continue; } // offset on the u axis - if (TokenMatch(filePtr, "UVW_U_OFFSET", 12)) { - ParseLV4MeshFloat(map.mOffsetU); + if (TokenMatch(mFilePtr, "UVW_U_OFFSET", 12)) { + ParseLV4MeshReal(map.mOffsetU); continue; } // offset on the v axis - if (TokenMatch(filePtr, "UVW_V_OFFSET", 12)) { - ParseLV4MeshFloat(map.mOffsetV); + if (TokenMatch(mFilePtr, "UVW_V_OFFSET", 12)) { + ParseLV4MeshReal(map.mOffsetV); continue; } // tiling on the u axis - if (TokenMatch(filePtr, "UVW_U_TILING", 12)) { - ParseLV4MeshFloat(map.mScaleU); + if (TokenMatch(mFilePtr, "UVW_U_TILING", 12)) { + ParseLV4MeshReal(map.mScaleU); continue; } // tiling on the v axis - if (TokenMatch(filePtr, "UVW_V_TILING", 12)) { - ParseLV4MeshFloat(map.mScaleV); + if (TokenMatch(mFilePtr, "UVW_V_TILING", 12)) { + ParseLV4MeshReal(map.mScaleV); continue; } // rotation around the z-axis - if (TokenMatch(filePtr, "UVW_ANGLE", 9)) { - ParseLV4MeshFloat(map.mRotation); + if (TokenMatch(mFilePtr, "UVW_ANGLE", 9)) { + ParseLV4MeshReal(map.mRotation); continue; } // map blending factor - if (TokenMatch(filePtr, "MAP_AMOUNT", 10)) { - ParseLV4MeshFloat(map.mTextureBlend); + if (TokenMatch(mFilePtr, "MAP_AMOUNT", 10)) { + ParseLV4MeshReal(map.mTextureBlend); continue; } } @@ -754,14 +767,14 @@ void Parser::ParseLV3MapBlock(Texture &map) { // ------------------------------------------------------------------------------------------------ bool Parser::ParseString(std::string &out, const char *szName) { char szBuffer[1024]; - if (!SkipSpaces(&filePtr, mEnd)) { + if (!SkipSpaces(&mFilePtr, mEnd)) { ai_snprintf(szBuffer, 1024, "Unable to parse %s block: Unexpected EOL", szName); LogWarning(szBuffer); return false; } // there must be '"' - if ('\"' != *filePtr) { + if ('\"' != *mFilePtr) { ai_snprintf(szBuffer, 1024, "Unable to parse %s block: Strings are expected " "to be enclosed in double quotation marks", @@ -769,8 +782,8 @@ bool Parser::ParseString(std::string &out, const char *szName) { LogWarning(szBuffer); return false; } - ++filePtr; - const char *sz = filePtr; + ++mFilePtr; + const char *sz = mFilePtr; while (true) { if ('\"' == *sz) break; @@ -784,8 +797,8 @@ bool Parser::ParseString(std::string &out, const char *szName) { } sz++; } - out = std::string(filePtr, (uintptr_t)sz - (uintptr_t)filePtr); - filePtr = sz + 1; + out = std::string(mFilePtr, (uintptr_t)sz - (uintptr_t)mFilePtr); + mFilePtr = sz + 1; return true; } @@ -793,48 +806,48 @@ bool Parser::ParseString(std::string &out, const char *szName) { void Parser::ParseLV1ObjectBlock(ASE::BaseNode &node) { AI_ASE_PARSER_INIT(); while (true) { - if ('*' == *filePtr) { - ++filePtr; + if ('*' == *mFilePtr) { + ++mFilePtr; // first process common tokens such as node name and transform // name of the mesh/node - if (TokenMatch(filePtr, "NODE_NAME", 9)) { + if (TokenMatch(mFilePtr, "NODE_NAME", 9)) { if (!ParseString(node.mName, "*NODE_NAME")) SkipToNextToken(); continue; } // name of the parent of the node - if (TokenMatch(filePtr, "NODE_PARENT", 11)) { + if (TokenMatch(mFilePtr, "NODE_PARENT", 11)) { if (!ParseString(node.mParent, "*NODE_PARENT")) SkipToNextToken(); continue; } // transformation matrix of the node - if (TokenMatch(filePtr, "NODE_TM", 7)) { + if (TokenMatch(mFilePtr, "NODE_TM", 7)) { ParseLV2NodeTransformBlock(node); continue; } // animation data of the node - if (TokenMatch(filePtr, "TM_ANIMATION", 12)) { + if (TokenMatch(mFilePtr, "TM_ANIMATION", 12)) { ParseLV2AnimationBlock(node); continue; } if (node.mType == BaseNode::Light) { // light settings - if (TokenMatch(filePtr, "LIGHT_SETTINGS", 14)) { + if (TokenMatch(mFilePtr, "LIGHT_SETTINGS", 14)) { ParseLV2LightSettingsBlock((ASE::Light &)node); continue; } // type of the light source - if (TokenMatch(filePtr, "LIGHT_TYPE", 10)) { - if (!ASSIMP_strincmp("omni", filePtr, 4)) { + if (TokenMatch(mFilePtr, "LIGHT_TYPE", 10)) { + if (!ASSIMP_strincmp("omni", mFilePtr, 4)) { ((ASE::Light &)node).mLightType = ASE::Light::OMNI; - } else if (!ASSIMP_strincmp("target", filePtr, 6)) { + } else if (!ASSIMP_strincmp("target", mFilePtr, 6)) { ((ASE::Light &)node).mLightType = ASE::Light::TARGET; - } else if (!ASSIMP_strincmp("free", filePtr, 4)) { + } else if (!ASSIMP_strincmp("free", mFilePtr, 4)) { ((ASE::Light &)node).mLightType = ASE::Light::FREE; - } else if (!ASSIMP_strincmp("directional", filePtr, 11)) { + } else if (!ASSIMP_strincmp("directional", mFilePtr, 11)) { ((ASE::Light &)node).mLightType = ASE::Light::DIRECTIONAL; } else { LogWarning("Unknown kind of light source"); @@ -843,13 +856,13 @@ void Parser::ParseLV1ObjectBlock(ASE::BaseNode &node) { } } else if (node.mType == BaseNode::Camera) { // Camera settings - if (TokenMatch(filePtr, "CAMERA_SETTINGS", 15)) { + if (TokenMatch(mFilePtr, "CAMERA_SETTINGS", 15)) { ParseLV2CameraSettingsBlock((ASE::Camera &)node); continue; - } else if (TokenMatch(filePtr, "CAMERA_TYPE", 11)) { - if (!ASSIMP_strincmp("target", filePtr, 6)) { + } else if (TokenMatch(mFilePtr, "CAMERA_TYPE", 11)) { + if (!ASSIMP_strincmp("target", mFilePtr, 6)) { ((ASE::Camera &)node).mCameraType = ASE::Camera::TARGET; - } else if (!ASSIMP_strincmp("free", filePtr, 4)) { + } else if (!ASSIMP_strincmp("free", mFilePtr, 4)) { ((ASE::Camera &)node).mCameraType = ASE::Camera::FREE; } else { LogWarning("Unknown kind of camera"); @@ -859,13 +872,13 @@ void Parser::ParseLV1ObjectBlock(ASE::BaseNode &node) { } else if (node.mType == BaseNode::Mesh) { // mesh data // FIX: Older files use MESH_SOFTSKIN - if (TokenMatch(filePtr, "MESH", 4) || - TokenMatch(filePtr, "MESH_SOFTSKIN", 13)) { + if (TokenMatch(mFilePtr, "MESH", 4) || + TokenMatch(mFilePtr, "MESH_SOFTSKIN", 13)) { ParseLV2MeshBlock((ASE::Mesh &)node); continue; } // mesh material index - if (TokenMatch(filePtr, "MATERIAL_REF", 12)) { + if (TokenMatch(mFilePtr, "MATERIAL_REF", 12)) { ParseLV4MeshLong(((ASE::Mesh &)node).iMaterialIndex); continue; } @@ -879,18 +892,18 @@ void Parser::ParseLV1ObjectBlock(ASE::BaseNode &node) { void Parser::ParseLV2CameraSettingsBlock(ASE::Camera &camera) { AI_ASE_PARSER_INIT(); while (true) { - if ('*' == *filePtr) { - ++filePtr; - if (TokenMatch(filePtr, "CAMERA_NEAR", 11)) { - ParseLV4MeshFloat(camera.mNear); + if ('*' == *mFilePtr) { + ++mFilePtr; + if (TokenMatch(mFilePtr, "CAMERA_NEAR", 11)) { + ParseLV4MeshReal(camera.mNear); continue; } - if (TokenMatch(filePtr, "CAMERA_FAR", 10)) { - ParseLV4MeshFloat(camera.mFar); + if (TokenMatch(mFilePtr, "CAMERA_FAR", 10)) { + ParseLV4MeshReal(camera.mFar); continue; } - if (TokenMatch(filePtr, "CAMERA_FOV", 10)) { - ParseLV4MeshFloat(camera.mFOV); + if (TokenMatch(mFilePtr, "CAMERA_FOV", 10)) { + ParseLV4MeshReal(camera.mFOV); continue; } } @@ -902,22 +915,22 @@ void Parser::ParseLV2CameraSettingsBlock(ASE::Camera &camera) { void Parser::ParseLV2LightSettingsBlock(ASE::Light &light) { AI_ASE_PARSER_INIT(); while (true) { - if ('*' == *filePtr) { - ++filePtr; - if (TokenMatch(filePtr, "LIGHT_COLOR", 11)) { + if ('*' == *mFilePtr) { + ++mFilePtr; + if (TokenMatch(mFilePtr, "LIGHT_COLOR", 11)) { ParseLV4MeshFloatTriple(&light.mColor.r); continue; } - if (TokenMatch(filePtr, "LIGHT_INTENS", 12)) { - ParseLV4MeshFloat(light.mIntensity); + if (TokenMatch(mFilePtr, "LIGHT_INTENS", 12)) { + ParseLV4MeshReal(light.mIntensity); continue; } - if (TokenMatch(filePtr, "LIGHT_HOTSPOT", 13)) { - ParseLV4MeshFloat(light.mAngle); + if (TokenMatch(mFilePtr, "LIGHT_HOTSPOT", 13)) { + ParseLV4MeshReal(light.mAngle); continue; } - if (TokenMatch(filePtr, "LIGHT_FALLOFF", 13)) { - ParseLV4MeshFloat(light.mFalloff); + if (TokenMatch(mFilePtr, "LIGHT_FALLOFF", 13)) { + ParseLV4MeshReal(light.mFalloff); continue; } } @@ -931,9 +944,9 @@ void Parser::ParseLV2AnimationBlock(ASE::BaseNode &mesh) { ASE::Animation *anim = &mesh.mAnim; while (true) { - if ('*' == *filePtr) { - ++filePtr; - if (TokenMatch(filePtr, "NODE_NAME", 9)) { + if ('*' == *mFilePtr) { + ++mFilePtr; + if (TokenMatch(mFilePtr, "NODE_NAME", 9)) { std::string temp; if (!ParseString(temp, "*NODE_NAME")) SkipToNextToken(); @@ -955,9 +968,9 @@ void Parser::ParseLV2AnimationBlock(ASE::BaseNode &mesh) { } // position keyframes - if (TokenMatch(filePtr, "CONTROL_POS_TRACK", 17) || - TokenMatch(filePtr, "CONTROL_POS_BEZIER", 18) || - TokenMatch(filePtr, "CONTROL_POS_TCB", 15)) { + if (TokenMatch(mFilePtr, "CONTROL_POS_TRACK", 17) || + TokenMatch(mFilePtr, "CONTROL_POS_BEZIER", 18) || + TokenMatch(mFilePtr, "CONTROL_POS_TCB", 15)) { if (!anim) SkipSection(); else @@ -965,9 +978,9 @@ void Parser::ParseLV2AnimationBlock(ASE::BaseNode &mesh) { continue; } // scaling keyframes - if (TokenMatch(filePtr, "CONTROL_SCALE_TRACK", 19) || - TokenMatch(filePtr, "CONTROL_SCALE_BEZIER", 20) || - TokenMatch(filePtr, "CONTROL_SCALE_TCB", 17)) { + if (TokenMatch(mFilePtr, "CONTROL_SCALE_TRACK", 19) || + TokenMatch(mFilePtr, "CONTROL_SCALE_BEZIER", 20) || + TokenMatch(mFilePtr, "CONTROL_SCALE_TCB", 17)) { if (!anim || anim == &mesh.mTargetAnim) { // Target animation channels may have no rotation channels ASSIMP_LOG_ERROR("ASE: Ignoring scaling channel in target animation"); @@ -977,9 +990,9 @@ void Parser::ParseLV2AnimationBlock(ASE::BaseNode &mesh) { continue; } // rotation keyframes - if (TokenMatch(filePtr, "CONTROL_ROT_TRACK", 17) || - TokenMatch(filePtr, "CONTROL_ROT_BEZIER", 18) || - TokenMatch(filePtr, "CONTROL_ROT_TCB", 15)) { + if (TokenMatch(mFilePtr, "CONTROL_ROT_TRACK", 17) || + TokenMatch(mFilePtr, "CONTROL_ROT_BEZIER", 18) || + TokenMatch(mFilePtr, "CONTROL_ROT_TCB", 15)) { if (!anim || anim == &mesh.mTargetAnim) { // Target animation channels may have no rotation channels ASSIMP_LOG_ERROR("ASE: Ignoring rotation channel in target animation"); @@ -998,8 +1011,8 @@ void Parser::ParseLV3ScaleAnimationBlock(ASE::Animation &anim) { unsigned int iIndex; while (true) { - if ('*' == *filePtr) { - ++filePtr; + if ('*' == *mFilePtr) { + ++mFilePtr; bool b = false; @@ -1007,25 +1020,25 @@ void Parser::ParseLV3ScaleAnimationBlock(ASE::Animation &anim) { // we ignore the additional information for bezier's and TCBs // simple scaling keyframe - if (TokenMatch(filePtr, "CONTROL_SCALE_SAMPLE", 20)) { + if (TokenMatch(mFilePtr, "CONTROL_SCALE_SAMPLE", 20)) { b = true; anim.mScalingType = ASE::Animation::TRACK; } // Bezier scaling keyframe - if (TokenMatch(filePtr, "CONTROL_BEZIER_SCALE_KEY", 24)) { + if (TokenMatch(mFilePtr, "CONTROL_BEZIER_SCALE_KEY", 24)) { b = true; anim.mScalingType = ASE::Animation::BEZIER; } // TCB scaling keyframe - if (TokenMatch(filePtr, "CONTROL_TCB_SCALE_KEY", 21)) { + if (TokenMatch(mFilePtr, "CONTROL_TCB_SCALE_KEY", 21)) { b = true; anim.mScalingType = ASE::Animation::TCB; } if (b) { anim.akeyScaling.emplace_back(); aiVectorKey &key = anim.akeyScaling.back(); - ParseLV4MeshFloatTriple(&key.mValue.x, iIndex); + ParseLV4MeshRealTriple(&key.mValue.x, iIndex); key.mTime = (double)iIndex; } } @@ -1037,8 +1050,8 @@ void Parser::ParseLV3PosAnimationBlock(ASE::Animation &anim) { AI_ASE_PARSER_INIT(); unsigned int iIndex; while (true) { - if ('*' == *filePtr) { - ++filePtr; + if ('*' == *mFilePtr) { + ++mFilePtr; bool b = false; @@ -1046,25 +1059,25 @@ void Parser::ParseLV3PosAnimationBlock(ASE::Animation &anim) { // we ignore the additional information for bezier's and TCBs // simple scaling keyframe - if (TokenMatch(filePtr, "CONTROL_POS_SAMPLE", 18)) { + if (TokenMatch(mFilePtr, "CONTROL_POS_SAMPLE", 18)) { b = true; anim.mPositionType = ASE::Animation::TRACK; } // Bezier scaling keyframe - if (TokenMatch(filePtr, "CONTROL_BEZIER_POS_KEY", 22)) { + if (TokenMatch(mFilePtr, "CONTROL_BEZIER_POS_KEY", 22)) { b = true; anim.mPositionType = ASE::Animation::BEZIER; } // TCB scaling keyframe - if (TokenMatch(filePtr, "CONTROL_TCB_POS_KEY", 19)) { + if (TokenMatch(mFilePtr, "CONTROL_TCB_POS_KEY", 19)) { b = true; anim.mPositionType = ASE::Animation::TCB; } if (b) { anim.akeyPositions.emplace_back(); aiVectorKey &key = anim.akeyPositions.back(); - ParseLV4MeshFloatTriple(&key.mValue.x, iIndex); + ParseLV4MeshRealTriple(&key.mValue.x, iIndex); key.mTime = (double)iIndex; } } @@ -1076,8 +1089,8 @@ void Parser::ParseLV3RotAnimationBlock(ASE::Animation &anim) { AI_ASE_PARSER_INIT(); unsigned int iIndex; while (true) { - if ('*' == *filePtr) { - ++filePtr; + if ('*' == *mFilePtr) { + ++mFilePtr; bool b = false; @@ -1085,18 +1098,18 @@ void Parser::ParseLV3RotAnimationBlock(ASE::Animation &anim) { // we ignore the additional information for bezier's and TCBs // simple scaling keyframe - if (TokenMatch(filePtr, "CONTROL_ROT_SAMPLE", 18)) { + if (TokenMatch(mFilePtr, "CONTROL_ROT_SAMPLE", 18)) { b = true; anim.mRotationType = ASE::Animation::TRACK; } // Bezier scaling keyframe - if (TokenMatch(filePtr, "CONTROL_BEZIER_ROT_KEY", 22)) { + if (TokenMatch(mFilePtr, "CONTROL_BEZIER_ROT_KEY", 22)) { b = true; anim.mRotationType = ASE::Animation::BEZIER; } // TCB scaling keyframe - if (TokenMatch(filePtr, "CONTROL_TCB_ROT_KEY", 19)) { + if (TokenMatch(mFilePtr, "CONTROL_TCB_ROT_KEY", 19)) { b = true; anim.mRotationType = ASE::Animation::TCB; } @@ -1105,8 +1118,8 @@ void Parser::ParseLV3RotAnimationBlock(ASE::Animation &anim) { aiQuatKey &key = anim.akeyRotations.back(); aiVector3D v; ai_real f; - ParseLV4MeshFloatTriple(&v.x, iIndex); - ParseLV4MeshFloat(f); + ParseLV4MeshRealTriple(&v.x, iIndex); + ParseLV4MeshReal(f); key.mTime = (double)iIndex; key.mValue = aiQuaternion(v, f); } @@ -1119,10 +1132,10 @@ void Parser::ParseLV2NodeTransformBlock(ASE::BaseNode &mesh) { AI_ASE_PARSER_INIT(); int mode = 0; while (true) { - if ('*' == *filePtr) { - ++filePtr; + if ('*' == *mFilePtr) { + ++mFilePtr; // name of the node - if (TokenMatch(filePtr, "NODE_NAME", 9)) { + if (TokenMatch(mFilePtr, "NODE_NAME", 9)) { std::string temp; if (!ParseString(temp, "*NODE_NAME")) SkipToNextToken(); @@ -1149,28 +1162,28 @@ void Parser::ParseLV2NodeTransformBlock(ASE::BaseNode &mesh) { if (mode) { // fourth row of the transformation matrix - and also the // only information here that is interesting for targets - if (TokenMatch(filePtr, "TM_ROW3", 7)) { - ParseLV4MeshFloatTriple((mode == 1 ? mesh.mTransform[3] : &mesh.mTargetPosition.x)); + if (TokenMatch(mFilePtr, "TM_ROW3", 7)) { + ParseLV4MeshRealTriple((mode == 1 ? mesh.mTransform[3] : &mesh.mTargetPosition.x)); continue; } if (mode == 1) { // first row of the transformation matrix - if (TokenMatch(filePtr, "TM_ROW0", 7)) { - ParseLV4MeshFloatTriple(mesh.mTransform[0]); + if (TokenMatch(mFilePtr, "TM_ROW0", 7)) { + ParseLV4MeshRealTriple(mesh.mTransform[0]); continue; } // second row of the transformation matrix - if (TokenMatch(filePtr, "TM_ROW1", 7)) { - ParseLV4MeshFloatTriple(mesh.mTransform[1]); + if (TokenMatch(mFilePtr, "TM_ROW1", 7)) { + ParseLV4MeshRealTriple(mesh.mTransform[1]); continue; } // third row of the transformation matrix - if (TokenMatch(filePtr, "TM_ROW2", 7)) { - ParseLV4MeshFloatTriple(mesh.mTransform[2]); + if (TokenMatch(mFilePtr, "TM_ROW2", 7)) { + ParseLV4MeshRealTriple(mesh.mTransform[2]); continue; } // inherited position axes - if (TokenMatch(filePtr, "INHERIT_POS", 11)) { + if (TokenMatch(mFilePtr, "INHERIT_POS", 11)) { unsigned int aiVal[3]; ParseLV4MeshLongTriple(aiVal); @@ -1179,7 +1192,7 @@ void Parser::ParseLV2NodeTransformBlock(ASE::BaseNode &mesh) { continue; } // inherited rotation axes - if (TokenMatch(filePtr, "INHERIT_ROT", 11)) { + if (TokenMatch(mFilePtr, "INHERIT_ROT", 11)) { unsigned int aiVal[3]; ParseLV4MeshLongTriple(aiVal); @@ -1188,7 +1201,7 @@ void Parser::ParseLV2NodeTransformBlock(ASE::BaseNode &mesh) { continue; } // inherited scaling axes - if (TokenMatch(filePtr, "INHERIT_SCL", 11)) { + if (TokenMatch(mFilePtr, "INHERIT_SCL", 11)) { unsigned int aiVal[3]; ParseLV4MeshLongTriple(aiVal); @@ -1213,75 +1226,75 @@ void Parser::ParseLV2MeshBlock(ASE::Mesh &mesh) { unsigned int iNumCVertices = 0; unsigned int iNumCFaces = 0; while (true) { - if ('*' == *filePtr) { - ++filePtr; + if ('*' == *mFilePtr) { + ++mFilePtr; // Number of vertices in the mesh - if (TokenMatch(filePtr, "MESH_NUMVERTEX", 14)) { + if (TokenMatch(mFilePtr, "MESH_NUMVERTEX", 14)) { ParseLV4MeshLong(iNumVertices); continue; } // Number of texture coordinates in the mesh - if (TokenMatch(filePtr, "MESH_NUMTVERTEX", 15)) { + if (TokenMatch(mFilePtr, "MESH_NUMTVERTEX", 15)) { ParseLV4MeshLong(iNumTVertices); continue; } // Number of vertex colors in the mesh - if (TokenMatch(filePtr, "MESH_NUMCVERTEX", 15)) { + if (TokenMatch(mFilePtr, "MESH_NUMCVERTEX", 15)) { ParseLV4MeshLong(iNumCVertices); continue; } // Number of regular faces in the mesh - if (TokenMatch(filePtr, "MESH_NUMFACES", 13)) { + if (TokenMatch(mFilePtr, "MESH_NUMFACES", 13)) { ParseLV4MeshLong(iNumFaces); continue; } // Number of UVWed faces in the mesh - if (TokenMatch(filePtr, "MESH_NUMTVFACES", 15)) { + if (TokenMatch(mFilePtr, "MESH_NUMTVFACES", 15)) { ParseLV4MeshLong(iNumTFaces); continue; } // Number of colored faces in the mesh - if (TokenMatch(filePtr, "MESH_NUMCVFACES", 15)) { + if (TokenMatch(mFilePtr, "MESH_NUMCVFACES", 15)) { ParseLV4MeshLong(iNumCFaces); continue; } // mesh vertex list block - if (TokenMatch(filePtr, "MESH_VERTEX_LIST", 16)) { + if (TokenMatch(mFilePtr, "MESH_VERTEX_LIST", 16)) { ParseLV3MeshVertexListBlock(iNumVertices, mesh); continue; } // mesh face list block - if (TokenMatch(filePtr, "MESH_FACE_LIST", 14)) { + if (TokenMatch(mFilePtr, "MESH_FACE_LIST", 14)) { ParseLV3MeshFaceListBlock(iNumFaces, mesh); continue; } // mesh texture vertex list block - if (TokenMatch(filePtr, "MESH_TVERTLIST", 14)) { + if (TokenMatch(mFilePtr, "MESH_TVERTLIST", 14)) { ParseLV3MeshTListBlock(iNumTVertices, mesh); continue; } // mesh texture face block - if (TokenMatch(filePtr, "MESH_TFACELIST", 14)) { + if (TokenMatch(mFilePtr, "MESH_TFACELIST", 14)) { ParseLV3MeshTFaceListBlock(iNumTFaces, mesh); continue; } // mesh color vertex list block - if (TokenMatch(filePtr, "MESH_CVERTLIST", 14)) { + if (TokenMatch(mFilePtr, "MESH_CVERTLIST", 14)) { ParseLV3MeshCListBlock(iNumCVertices, mesh); continue; } // mesh color face block - if (TokenMatch(filePtr, "MESH_CFACELIST", 14)) { + if (TokenMatch(mFilePtr, "MESH_CFACELIST", 14)) { ParseLV3MeshCFaceListBlock(iNumCFaces, mesh); continue; } // mesh normals - if (TokenMatch(filePtr, "MESH_NORMALS", 12)) { + if (TokenMatch(mFilePtr, "MESH_NORMALS", 12)) { ParseLV3MeshNormalListBlock(mesh); continue; } // another mesh UV channel ... - if (TokenMatch(filePtr, "MESH_MAPPINGCHANNEL", 19)) { + if (TokenMatch(mFilePtr, "MESH_MAPPINGCHANNEL", 19)) { unsigned int iIndex(0); ParseLV4MeshLong(iIndex); if (0 == iIndex) { @@ -1306,7 +1319,7 @@ void Parser::ParseLV2MeshBlock(ASE::Mesh &mesh) { } } // mesh animation keyframe. Not supported - if (TokenMatch(filePtr, "MESH_ANIMATION", 14)) { + if (TokenMatch(mFilePtr, "MESH_ANIMATION", 14)) { LogWarning("Found *MESH_ANIMATION element in ASE/ASK file. " "Keyframe animation is not supported by Assimp, this element " @@ -1314,7 +1327,7 @@ void Parser::ParseLV2MeshBlock(ASE::Mesh &mesh) { //SkipSection(); continue; } - if (TokenMatch(filePtr, "MESH_WEIGHTS", 12)) { + if (TokenMatch(mFilePtr, "MESH_WEIGHTS", 12)) { ParseLV3MeshWeightsBlock(mesh); continue; } @@ -1328,26 +1341,26 @@ void Parser::ParseLV3MeshWeightsBlock(ASE::Mesh &mesh) { unsigned int iNumVertices = 0, iNumBones = 0; while (true) { - if ('*' == *filePtr) { - ++filePtr; + if ('*' == *mFilePtr) { + ++mFilePtr; // Number of bone vertices ... - if (TokenMatch(filePtr, "MESH_NUMVERTEX", 14)) { + if (TokenMatch(mFilePtr, "MESH_NUMVERTEX", 14)) { ParseLV4MeshLong(iNumVertices); continue; } // Number of bones - if (TokenMatch(filePtr, "MESH_NUMBONE", 12)) { + if (TokenMatch(mFilePtr, "MESH_NUMBONE", 12)) { ParseLV4MeshLong(iNumBones); continue; } // parse the list of bones - if (TokenMatch(filePtr, "MESH_BONE_LIST", 14)) { + if (TokenMatch(mFilePtr, "MESH_BONE_LIST", 14)) { ParseLV4MeshBones(iNumBones, mesh); continue; } // parse the list of bones vertices - if (TokenMatch(filePtr, "MESH_BONE_VERTEX_LIST", 21)) { + if (TokenMatch(mFilePtr, "MESH_BONE_VERTEX_LIST", 21)) { ParseLV4MeshBonesVertices(iNumVertices, mesh); continue; } @@ -1360,14 +1373,14 @@ void Parser::ParseLV4MeshBones(unsigned int iNumBones, ASE::Mesh &mesh) { AI_ASE_PARSER_INIT(); mesh.mBones.resize(iNumBones, Bone("UNNAMED")); while (true) { - if ('*' == *filePtr) { - ++filePtr; + if ('*' == *mFilePtr) { + ++mFilePtr; // Mesh bone with name ... - if (TokenMatch(filePtr, "MESH_BONE_NAME", 14)) { + if (TokenMatch(mFilePtr, "MESH_BONE_NAME", 14)) { // parse an index ... - if (SkipSpaces(&filePtr, mEnd)) { - unsigned int iIndex = strtoul10(filePtr, &filePtr); + if (SkipSpaces(&mFilePtr, mEnd)) { + unsigned int iIndex = strtoul10(mFilePtr, &mFilePtr); if (iIndex >= iNumBones) { LogWarning("Bone index is out of bounds"); continue; @@ -1386,13 +1399,13 @@ void Parser::ParseLV4MeshBonesVertices(unsigned int iNumVertices, ASE::Mesh &mes AI_ASE_PARSER_INIT(); mesh.mBoneVertices.resize(iNumVertices); while (true) { - if ('*' == *filePtr) { - ++filePtr; + if ('*' == *mFilePtr) { + ++mFilePtr; // Mesh bone vertex - if (TokenMatch(filePtr, "MESH_BONE_VERTEX", 16)) { + if (TokenMatch(mFilePtr, "MESH_BONE_VERTEX", 16)) { // read the vertex index - unsigned int iIndex = strtoul10(filePtr, &filePtr); + unsigned int iIndex = strtoul10(mFilePtr, &mFilePtr); if (iIndex >= mesh.mPositions.size()) { iIndex = (unsigned int)mesh.mPositions.size() - 1; LogWarning("Bone vertex index is out of bounds. Using the largest valid " @@ -1401,17 +1414,17 @@ void Parser::ParseLV4MeshBonesVertices(unsigned int iNumVertices, ASE::Mesh &mes // --- ignored ai_real afVert[3]; - ParseLV4MeshFloatTriple(afVert); + ParseLV4MeshRealTriple(afVert); std::pair pairOut; while (true) { // first parse the bone index ... - if (!SkipSpaces(&filePtr, mEnd)) break; - pairOut.first = strtoul10(filePtr, &filePtr); + if (!SkipSpaces(&mFilePtr, mEnd)) break; + pairOut.first = strtoul10(mFilePtr, &mFilePtr); // then parse the vertex weight - if (!SkipSpaces(&filePtr, mEnd)) break; - filePtr = fast_atoreal_move(filePtr, pairOut.second); + if (!SkipSpaces(&mFilePtr, mEnd)) break; + mFilePtr = fast_atoreal_move(mFilePtr, pairOut.second); // -1 marks unused entries if (-1 != pairOut.first) { @@ -1432,15 +1445,15 @@ void Parser::ParseLV3MeshVertexListBlock( // allocate enough storage in the array mesh.mPositions.resize(iNumVertices); while (true) { - if ('*' == *filePtr) { - ++filePtr; + if ('*' == *mFilePtr) { + ++mFilePtr; // Vertex entry - if (TokenMatch(filePtr, "MESH_VERTEX", 11)) { + if (TokenMatch(mFilePtr, "MESH_VERTEX", 11)) { aiVector3D vTemp; unsigned int iIndex; - ParseLV4MeshFloatTriple(&vTemp.x, iIndex); + ParseLV4MeshRealTriple(&vTemp.x, iIndex); if (iIndex >= iNumVertices) { LogWarning("Invalid vertex index. It will be ignored"); @@ -1459,11 +1472,11 @@ void Parser::ParseLV3MeshFaceListBlock(unsigned int iNumFaces, ASE::Mesh &mesh) // allocate enough storage in the face array mesh.mFaces.resize(iNumFaces); while (true) { - if ('*' == *filePtr) { - ++filePtr; + if ('*' == *mFilePtr) { + ++mFilePtr; // Face entry - if (TokenMatch(filePtr, "MESH_FACE", 9)) { + if (TokenMatch(mFilePtr, "MESH_FACE", 9)) { ASE::Face mFace; ParseLV4MeshFace(mFace); @@ -1486,14 +1499,14 @@ void Parser::ParseLV3MeshTListBlock(unsigned int iNumVertices, // allocate enough storage in the array mesh.amTexCoords[iChannel].resize(iNumVertices); while (true) { - if ('*' == *filePtr) { - ++filePtr; + if ('*' == *mFilePtr) { + ++mFilePtr; // Vertex entry - if (TokenMatch(filePtr, "MESH_TVERT", 10)) { + if (TokenMatch(mFilePtr, "MESH_TVERT", 10)) { aiVector3D vTemp; unsigned int iIndex; - ParseLV4MeshFloatTriple(&vTemp.x, iIndex); + ParseLV4MeshRealTriple(&vTemp.x, iIndex); if (iIndex >= iNumVertices) { LogWarning("Tvertex has an invalid index. It will be ignored"); @@ -1515,11 +1528,11 @@ void Parser::ParseLV3MeshTFaceListBlock(unsigned int iNumFaces, ASE::Mesh &mesh, unsigned int iChannel) { AI_ASE_PARSER_INIT(); while (true) { - if ('*' == *filePtr) { - ++filePtr; + if ('*' == *mFilePtr) { + ++mFilePtr; // Face entry - if (TokenMatch(filePtr, "MESH_TFACE", 10)) { + if (TokenMatch(mFilePtr, "MESH_TFACE", 10)) { unsigned int aiValues[3]; unsigned int iIndex = 0; @@ -1545,26 +1558,26 @@ void Parser::ParseLV3MappingChannel(unsigned int iChannel, ASE::Mesh &mesh) { unsigned int iNumTVertices = 0; unsigned int iNumTFaces = 0; while (true) { - if ('*' == *filePtr) { - ++filePtr; + if ('*' == *mFilePtr) { + ++mFilePtr; // Number of texture coordinates in the mesh - if (TokenMatch(filePtr, "MESH_NUMTVERTEX", 15)) { + if (TokenMatch(mFilePtr, "MESH_NUMTVERTEX", 15)) { ParseLV4MeshLong(iNumTVertices); continue; } // Number of UVWed faces in the mesh - if (TokenMatch(filePtr, "MESH_NUMTVFACES", 15)) { + if (TokenMatch(mFilePtr, "MESH_NUMTVFACES", 15)) { ParseLV4MeshLong(iNumTFaces); continue; } // mesh texture vertex list block - if (TokenMatch(filePtr, "MESH_TVERTLIST", 14)) { + if (TokenMatch(mFilePtr, "MESH_TVERTLIST", 14)) { ParseLV3MeshTListBlock(iNumTVertices, mesh, iChannel); continue; } // mesh texture face block - if (TokenMatch(filePtr, "MESH_TFACELIST", 14)) { + if (TokenMatch(mFilePtr, "MESH_TFACELIST", 14)) { ParseLV3MeshTFaceListBlock(iNumTFaces, mesh, iChannel); continue; } @@ -1579,11 +1592,11 @@ void Parser::ParseLV3MeshCListBlock(unsigned int iNumVertices, ASE::Mesh &mesh) // allocate enough storage in the array mesh.mVertexColors.resize(iNumVertices); while (true) { - if ('*' == *filePtr) { - ++filePtr; + if ('*' == *mFilePtr) { + ++mFilePtr; // Vertex entry - if (TokenMatch(filePtr, "MESH_VERTCOL", 12)) { + if (TokenMatch(mFilePtr, "MESH_VERTCOL", 12)) { aiColor4D vTemp; vTemp.a = 1.0f; unsigned int iIndex; @@ -1603,11 +1616,11 @@ void Parser::ParseLV3MeshCListBlock(unsigned int iNumVertices, ASE::Mesh &mesh) void Parser::ParseLV3MeshCFaceListBlock(unsigned int iNumFaces, ASE::Mesh &mesh) { AI_ASE_PARSER_INIT(); while (true) { - if ('*' == *filePtr) { - ++filePtr; + if ('*' == *mFilePtr) { + ++mFilePtr; // Face entry - if (TokenMatch(filePtr, "MESH_CFACE", 10)) { + if (TokenMatch(mFilePtr, "MESH_CFACE", 10)) { unsigned int aiValues[3]; unsigned int iIndex = 0; @@ -1640,11 +1653,11 @@ void Parser::ParseLV3MeshNormalListBlock(ASE::Mesh &sMesh) { // Smooth the vertex and face normals together. The result // will be edgy then, but otherwise everything would be soft ... while (true) { - if ('*' == *filePtr) { - ++filePtr; - if (faceIdx != UINT_MAX && TokenMatch(filePtr, "MESH_VERTEXNORMAL", 17)) { + if ('*' == *mFilePtr) { + ++mFilePtr; + if (faceIdx != UINT_MAX && TokenMatch(mFilePtr, "MESH_VERTEXNORMAL", 17)) { aiVector3D vNormal; - ParseLV4MeshFloatTriple(&vNormal.x, index); + ParseLV4MeshRealTriple(&vNormal.x, index); if (faceIdx >= sMesh.mFaces.size()) continue; @@ -1664,9 +1677,9 @@ void Parser::ParseLV3MeshNormalListBlock(ASE::Mesh &sMesh) { sMesh.mNormals[faceIdx * 3 + index] += vNormal; continue; } - if (TokenMatch(filePtr, "MESH_FACENORMAL", 15)) { + if (TokenMatch(mFilePtr, "MESH_FACENORMAL", 15)) { aiVector3D vNormal; - ParseLV4MeshFloatTriple(&vNormal.x, faceIdx); + ParseLV4MeshRealTriple(&vNormal.x, faceIdx); if (faceIdx >= sMesh.mFaces.size()) { ASSIMP_LOG_ERROR("ASE: Invalid vertex index in MESH_FACENORMAL section"); @@ -1686,34 +1699,34 @@ void Parser::ParseLV3MeshNormalListBlock(ASE::Mesh &sMesh) { // ------------------------------------------------------------------------------------------------ void Parser::ParseLV4MeshFace(ASE::Face &out) { // skip spaces and tabs - if (!SkipSpaces(&filePtr, mEnd)) { + if (!SkipSpaces(&mFilePtr, mEnd)) { LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL [#1]"); SkipToNextToken(); return; } // parse the face index - out.iFace = strtoul10(filePtr, &filePtr); + out.iFace = strtoul10(mFilePtr, &mFilePtr); // next character should be ':' - if (!SkipSpaces(&filePtr, mEnd)) { + if (!SkipSpaces(&mFilePtr, mEnd)) { // FIX: there are some ASE files which haven't got : here .... LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL. \':\' expected [#2]"); SkipToNextToken(); return; } // FIX: There are some ASE files which haven't got ':' here - if (':' == *filePtr) ++filePtr; + if (':' == *mFilePtr) ++mFilePtr; // Parse all mesh indices for (unsigned int i = 0; i < 3; ++i) { unsigned int iIndex = 0; - if (!SkipSpaces(&filePtr, mEnd)) { + if (!SkipSpaces(&mFilePtr, mEnd)) { LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL"); SkipToNextToken(); return; } - switch (*filePtr) { + switch (*mFilePtr) { case 'A': case 'a': break; @@ -1731,39 +1744,39 @@ void Parser::ParseLV4MeshFace(ASE::Face &out) { SkipToNextToken(); return; }; - ++filePtr; + ++mFilePtr; // next character should be ':' - if (!SkipSpaces(&filePtr, mEnd) || ':' != *filePtr) { + if (!SkipSpaces(&mFilePtr, mEnd) || ':' != *mFilePtr) { LogWarning("Unable to parse *MESH_FACE Element: " "Unexpected EOL. \':\' expected [#2]"); SkipToNextToken(); return; } - ++filePtr; - if (!SkipSpaces(&filePtr, mEnd)) { + ++mFilePtr; + if (!SkipSpaces(&mFilePtr, mEnd)) { LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL. " "Vertex index expected [#4]"); SkipToNextToken(); return; } - out.mIndices[iIndex] = strtoul10(filePtr, &filePtr); + out.mIndices[iIndex] = strtoul10(mFilePtr, &mFilePtr); } // now we need to skip the AB, BC, CA blocks. while (true) { - if ('*' == *filePtr) break; - if (IsLineEnd(*filePtr)) { + if ('*' == *mFilePtr) break; + if (IsLineEnd(*mFilePtr)) { //iLineNumber++; return; } - filePtr++; + mFilePtr++; } // parse the smoothing group of the face - if (TokenMatch(filePtr, "*MESH_SMOOTHING", 15)) { - if (!SkipSpaces(&filePtr, mEnd)) { + if (TokenMatch(mFilePtr, "*MESH_SMOOTHING", 15)) { + if (!SkipSpaces(&mFilePtr, mEnd)) { LogWarning("Unable to parse *MESH_SMOOTHING Element: " "Unexpected EOL. Smoothing group(s) expected [#5]"); SkipToNextToken(); @@ -1773,43 +1786,43 @@ void Parser::ParseLV4MeshFace(ASE::Face &out) { // Parse smoothing groups until we don't anymore see commas // FIX: There needn't always be a value, sad but true while (true) { - if (*filePtr < '9' && *filePtr >= '0') { - uint32_t value = strtoul10(filePtr, &filePtr); + if (*mFilePtr < '9' && *mFilePtr >= '0') { + uint32_t value = strtoul10(mFilePtr, &mFilePtr); if (value < 32) { - out.iSmoothGroup |= (1 << strtoul10(filePtr, &filePtr)); + out.iSmoothGroup |= (1 << strtoul10(mFilePtr, &mFilePtr)); } else { const std::string message = std::string("Unable to set smooth group, value with ") + ai_to_string(value) + std::string(" out of range"); LogWarning(message.c_str()); } } - SkipSpaces(&filePtr, mEnd); - if (',' != *filePtr) { + SkipSpaces(&mFilePtr, mEnd); + if (',' != *mFilePtr) { break; } - ++filePtr; - SkipSpaces(&filePtr, mEnd); + ++mFilePtr; + SkipSpaces(&mFilePtr, mEnd); } } // *MESH_MTLID is optional, too while (true) { - if ('*' == *filePtr) { + if ('*' == *mFilePtr) { break; } - if (IsLineEnd(*filePtr)) { + if (IsLineEnd(*mFilePtr)) { return; } - filePtr++; + mFilePtr++; } - if (TokenMatch(filePtr, "*MESH_MTLID", 11)) { - if (!SkipSpaces(&filePtr, mEnd)) { + if (TokenMatch(mFilePtr, "*MESH_MTLID", 11)) { + if (!SkipSpaces(&mFilePtr, mEnd)) { LogWarning("Unable to parse *MESH_MTLID Element: Unexpected EOL. " "Material index expected [#6]"); SkipToNextToken(); return; } - out.iMaterial = strtoul10(filePtr, &filePtr); + out.iMaterial = strtoul10(mFilePtr, &mFilePtr); } return; } @@ -1831,7 +1844,17 @@ void Parser::ParseLV4MeshLongTriple(unsigned int *apOut, unsigned int &rIndexOut ParseLV4MeshLongTriple(apOut); } // ------------------------------------------------------------------------------------------------ -void Parser::ParseLV4MeshFloatTriple(ai_real *apOut, unsigned int &rIndexOut) { +void Parser::ParseLV4MeshRealTriple(ai_real *apOut, unsigned int &rIndexOut) { + ai_assert(nullptr != apOut); + + // parse the index + ParseLV4MeshLong(rIndexOut); + + // parse the three others + ParseLV4MeshRealTriple(apOut); +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshFloatTriple(float* apOut, unsigned int& rIndexOut) { ai_assert(nullptr != apOut); // parse the index @@ -1841,7 +1864,15 @@ void Parser::ParseLV4MeshFloatTriple(ai_real *apOut, unsigned int &rIndexOut) { ParseLV4MeshFloatTriple(apOut); } // ------------------------------------------------------------------------------------------------ -void Parser::ParseLV4MeshFloatTriple(ai_real *apOut) { +void Parser::ParseLV4MeshRealTriple(ai_real *apOut) { + ai_assert(nullptr != apOut); + + for (unsigned int i = 0; i < 3; ++i) { + ParseLV4MeshReal(apOut[i]); + } +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshFloatTriple(float* apOut) { ai_assert(nullptr != apOut); for (unsigned int i = 0; i < 3; ++i) { @@ -1849,9 +1880,9 @@ void Parser::ParseLV4MeshFloatTriple(ai_real *apOut) { } } // ------------------------------------------------------------------------------------------------ -void Parser::ParseLV4MeshFloat(ai_real &fOut) { +void Parser::ParseLV4MeshReal(ai_real &fOut) { // skip spaces and tabs - if (!SkipSpaces(&filePtr, mEnd)) { + if (!SkipSpaces(&mFilePtr, mEnd)) { // LOG LogWarning("Unable to parse float: unexpected EOL [#1]"); fOut = 0.0; @@ -1859,12 +1890,25 @@ void Parser::ParseLV4MeshFloat(ai_real &fOut) { return; } // parse the first float - filePtr = fast_atoreal_move(filePtr, fOut); + mFilePtr = fast_atoreal_move(mFilePtr, fOut); +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshFloat(float &fOut) { + // skip spaces and tabs + if (!SkipSpaces(&mFilePtr, mEnd)) { + // LOG + LogWarning("Unable to parse float: unexpected EOL [#1]"); + fOut = 0.0; + ++iLineNumber; + return; + } + // parse the first float + mFilePtr = fast_atoreal_move(mFilePtr, fOut); } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV4MeshLong(unsigned int &iOut) { // Skip spaces and tabs - if (!SkipSpaces(&filePtr, mEnd)) { + if (!SkipSpaces(&mFilePtr, mEnd)) { // LOG LogWarning("Unable to parse long: unexpected EOL [#1]"); iOut = 0; @@ -1872,7 +1916,7 @@ void Parser::ParseLV4MeshLong(unsigned int &iOut) { return; } // parse the value - iOut = strtoul10(filePtr, &filePtr); + iOut = strtoul10(mFilePtr, &mFilePtr); } } diff --git a/code/AssetLib/ASE/ASEParser.h b/code/AssetLib/ASE/ASEParser.h index 263b5ca73..916605790 100644 --- a/code/AssetLib/ASE/ASEParser.h +++ b/code/AssetLib/ASE/ASEParser.h @@ -391,11 +391,11 @@ public: // ------------------------------------------------------------------- //! Construct a parser from a given input file which is //! guaranteed to be terminated with zero. - //! @param szFile Input file + //! @param file The name of the input file. //! @param fileFormatDefault Assumed file format version. If the //! file format is specified in the file the new value replaces //! the default value. - Parser(const char *szFile, unsigned int fileFormatDefault); + Parser(const char *file, size_t fileLen, unsigned int fileFormatDefault); // ------------------------------------------------------------------- //! Parses the file into the parsers internal representation @@ -553,13 +553,15 @@ private: //! (also works for MESH_TVERT, MESH_CFACE, MESH_VERTCOL ...) //! \param apOut Output buffer (3 floats) //! \param rIndexOut Output index - void ParseLV4MeshFloatTriple(ai_real *apOut, unsigned int &rIndexOut); + void ParseLV4MeshRealTriple(ai_real *apOut, unsigned int &rIndexOut); + void ParseLV4MeshFloatTriple(float *apOut, unsigned int &rIndexOut); // ------------------------------------------------------------------- //! Parse a *MESH_VERT block in a file //! (also works for MESH_TVERT, MESH_CFACE, MESH_VERTCOL ...) //! \param apOut Output buffer (3 floats) - void ParseLV4MeshFloatTriple(ai_real *apOut); + void ParseLV4MeshRealTriple(ai_real *apOut); + void ParseLV4MeshFloatTriple(float *apOut); // ------------------------------------------------------------------- //! Parse a *MESH_TFACE block in a file @@ -577,7 +579,8 @@ private: // ------------------------------------------------------------------- //! Parse a single float element //! \param fOut Output float - void ParseLV4MeshFloat(ai_real &fOut); + void ParseLV4MeshReal(ai_real &fOut); + void ParseLV4MeshFloat(float &fOut); // ------------------------------------------------------------------- //! Parse a single int element @@ -617,11 +620,8 @@ private: bool ParseString(std::string &out, const char *szName); public: - //! Pointer to current data - const char *filePtr; - - /// The end pointer of the file data - const char *mEnd; + const char *mFilePtr; ////< Pointer to current data + const char *mEnd; ///< The end pointer of the file data //! background color to be passed to the viewer //! QNAN if none was found diff --git a/code/AssetLib/Blender/BlenderLoader.cpp b/code/AssetLib/Blender/BlenderLoader.cpp index 1a40a2fe5..923eb5959 100644 --- a/code/AssetLib/Blender/BlenderLoader.cpp +++ b/code/AssetLib/Blender/BlenderLoader.cpp @@ -359,7 +359,7 @@ void BlenderImporter::ResolveImage(aiMaterial *out, const Material *mat, const M // check if the file contents are bundled with the BLEND file if (img->packedfile) { name.data[0] = '*'; - name.length = 1 + ASSIMP_itoa10(name.data + 1, static_cast(MAXLEN - 1), static_cast(conv_data.textures->size())); + name.length = 1 + ASSIMP_itoa10(name.data + 1, static_cast(AI_MAXLEN - 1), static_cast(conv_data.textures->size())); conv_data.textures->push_back(new aiTexture()); aiTexture *curTex = conv_data.textures->back(); @@ -433,7 +433,7 @@ void BlenderImporter::AddSentinelTexture(aiMaterial *out, const Material *mat, c (void)conv_data; aiString name; - name.length = ai_snprintf(name.data, MAXLEN, "Procedural,num=%i,type=%s", conv_data.sentinel_cnt++, + name.length = ai_snprintf(name.data, AI_MAXLEN, "Procedural,num=%i,type=%s", conv_data.sentinel_cnt++, GetTextureTypeDisplayString(tex->tex->type)); out->AddProperty(&name, AI_MATKEY_TEXTURE_DIFFUSE( conv_data.next_texture[aiTextureType_DIFFUSE]++)); diff --git a/code/AssetLib/Collada/ColladaLoader.cpp b/code/AssetLib/Collada/ColladaLoader.cpp index 17c4f4b28..6d7085b35 100644 --- a/code/AssetLib/Collada/ColladaLoader.cpp +++ b/code/AssetLib/Collada/ColladaLoader.cpp @@ -247,7 +247,9 @@ aiNode *ColladaLoader::BuildHierarchy(const ColladaParser &pParser, const Collad // add children. first the *real* ones node->mNumChildren = static_cast(pNode->mChildren.size() + instances.size()); - node->mChildren = new aiNode *[node->mNumChildren]; + if (node->mNumChildren != 0) { + node->mChildren = new aiNode * [node->mNumChildren]; + } for (size_t a = 0; a < pNode->mChildren.size(); ++a) { node->mChildren[a] = BuildHierarchy(pParser, pNode->mChildren[a]); @@ -623,16 +625,14 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Mesh *pSrc } // same for texture coords, as many as we have - // empty slots are not allowed, need to pack and adjust UV indexes accordingly - for (size_t a = 0, real = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) { + for (size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) { if (pSrcMesh->mTexCoords[a].size() >= pStartVertex + numVertices) { - dstMesh->mTextureCoords[real] = new aiVector3D[numVertices]; + dstMesh->mTextureCoords[a] = new aiVector3D[numVertices]; for (size_t b = 0; b < numVertices; ++b) { - dstMesh->mTextureCoords[real][b] = pSrcMesh->mTexCoords[a][pStartVertex + b]; + dstMesh->mTextureCoords[a][b] = pSrcMesh->mTexCoords[a][pStartVertex + b]; } - dstMesh->mNumUVComponents[real] = pSrcMesh->mNumUVComponents[a]; - ++real; + dstMesh->mNumUVComponents[a] = pSrcMesh->mNumUVComponents[a]; } } diff --git a/code/AssetLib/Collada/ColladaParser.cpp b/code/AssetLib/Collada/ColladaParser.cpp index c5163fe39..0741b3c73 100644 --- a/code/AssetLib/Collada/ColladaParser.cpp +++ b/code/AssetLib/Collada/ColladaParser.cpp @@ -635,7 +635,8 @@ void ColladaParser::ReadController(XmlNode &node, Collada::Controller &controlle const std::string ¤tName = currentNode.name(); if (currentName == "morph") { controller.mType = Morph; - controller.mMeshId = currentNode.attribute("source").as_string(); + std::string id = currentNode.attribute("source").as_string(); + controller.mMeshId = id.substr(1, id.size() - 1); int methodIndex = currentNode.attribute("method").as_int(); if (methodIndex > 0) { std::string method; @@ -967,34 +968,34 @@ void ColladaParser::ReadLight(XmlNode &node, Collada::Light &pLight) { content = fast_atoreal_move(content, (ai_real &)pLight.mColor.b); SkipSpacesAndLineEnd(&content, end); } else if (currentName == "constant_attenuation") { - XmlParser::getValueAsFloat(currentNode, pLight.mAttConstant); + XmlParser::getValueAsReal(currentNode, pLight.mAttConstant); } else if (currentName == "linear_attenuation") { - XmlParser::getValueAsFloat(currentNode, pLight.mAttLinear); + XmlParser::getValueAsReal(currentNode, pLight.mAttLinear); } else if (currentName == "quadratic_attenuation") { - XmlParser::getValueAsFloat(currentNode, pLight.mAttQuadratic); + XmlParser::getValueAsReal(currentNode, pLight.mAttQuadratic); } else if (currentName == "falloff_angle") { - XmlParser::getValueAsFloat(currentNode, pLight.mFalloffAngle); + XmlParser::getValueAsReal(currentNode, pLight.mFalloffAngle); } else if (currentName == "falloff_exponent") { - XmlParser::getValueAsFloat(currentNode, pLight.mFalloffExponent); + XmlParser::getValueAsReal(currentNode, pLight.mFalloffExponent); } // FCOLLADA extensions // ------------------------------------------------------- else if (currentName == "outer_cone") { - XmlParser::getValueAsFloat(currentNode, pLight.mOuterAngle); + XmlParser::getValueAsReal(currentNode, pLight.mOuterAngle); } else if (currentName == "penumbra_angle") { // this one is deprecated, now calculated using outer_cone - XmlParser::getValueAsFloat(currentNode, pLight.mPenumbraAngle); + XmlParser::getValueAsReal(currentNode, pLight.mPenumbraAngle); } else if (currentName == "intensity") { - XmlParser::getValueAsFloat(currentNode, pLight.mIntensity); + XmlParser::getValueAsReal(currentNode, pLight.mIntensity); } else if (currentName == "falloff") { - XmlParser::getValueAsFloat(currentNode, pLight.mOuterAngle); + XmlParser::getValueAsReal(currentNode, pLight.mOuterAngle); } else if (currentName == "hotspot_beam") { - XmlParser::getValueAsFloat(currentNode, pLight.mFalloffAngle); + XmlParser::getValueAsReal(currentNode, pLight.mFalloffAngle); } // OpenCOLLADA extensions // ------------------------------------------------------- else if (currentName == "decay_falloff") { - XmlParser::getValueAsFloat(currentNode, pLight.mOuterAngle); + XmlParser::getValueAsReal(currentNode, pLight.mOuterAngle); } } } @@ -1009,15 +1010,15 @@ void ColladaParser::ReadCamera(XmlNode &node, Collada::Camera &camera) { if (currentName == "orthographic") { camera.mOrtho = true; } else if (currentName == "xfov" || currentName == "xmag") { - XmlParser::getValueAsFloat(currentNode, camera.mHorFov); + XmlParser::getValueAsReal(currentNode, camera.mHorFov); } else if (currentName == "yfov" || currentName == "ymag") { - XmlParser::getValueAsFloat(currentNode, camera.mVerFov); + XmlParser::getValueAsReal(currentNode, camera.mVerFov); } else if (currentName == "aspect_ratio") { - XmlParser::getValueAsFloat(currentNode, camera.mAspect); + XmlParser::getValueAsReal(currentNode, camera.mAspect); } else if (currentName == "znear") { - XmlParser::getValueAsFloat(currentNode, camera.mZNear); + XmlParser::getValueAsReal(currentNode, camera.mZNear); } else if (currentName == "zfar") { - XmlParser::getValueAsFloat(currentNode, camera.mZFar); + XmlParser::getValueAsReal(currentNode, camera.mZFar); } } } @@ -1169,15 +1170,15 @@ void ColladaParser::ReadSamplerProperties(XmlNode &node, Sampler &out) { } else if (currentName == "mirrorV") { XmlParser::getValueAsBool(currentNode, out.mMirrorV); } else if (currentName == "repeatU") { - XmlParser::getValueAsFloat(currentNode, out.mTransform.mScaling.x); + XmlParser::getValueAsReal(currentNode, out.mTransform.mScaling.x); } else if (currentName == "repeatV") { - XmlParser::getValueAsFloat(currentNode, out.mTransform.mScaling.y); + XmlParser::getValueAsReal(currentNode, out.mTransform.mScaling.y); } else if (currentName == "offsetU") { - XmlParser::getValueAsFloat(currentNode, out.mTransform.mTranslation.x); + XmlParser::getValueAsReal(currentNode, out.mTransform.mTranslation.x); } else if (currentName == "offsetV") { - XmlParser::getValueAsFloat(currentNode, out.mTransform.mTranslation.y); + XmlParser::getValueAsReal(currentNode, out.mTransform.mTranslation.y); } else if (currentName == "rotateUV") { - XmlParser::getValueAsFloat(currentNode, out.mTransform.mRotation); + XmlParser::getValueAsReal(currentNode, out.mTransform.mRotation); } else if (currentName == "blend_mode") { std::string v; XmlParser::getValueAsString(currentNode, v); @@ -1197,14 +1198,14 @@ void ColladaParser::ReadSamplerProperties(XmlNode &node, Sampler &out) { // OKINO extensions // ------------------------------------------------------- else if (currentName == "weighting") { - XmlParser::getValueAsFloat(currentNode, out.mWeighting); + XmlParser::getValueAsReal(currentNode, out.mWeighting); } else if (currentName == "mix_with_previous_layer") { - XmlParser::getValueAsFloat(currentNode, out.mMixWithPrevious); + XmlParser::getValueAsReal(currentNode, out.mMixWithPrevious); } // MAX3D extensions // ------------------------------------------------------- else if (currentName == "amount") { - XmlParser::getValueAsFloat(currentNode, out.mWeighting); + XmlParser::getValueAsReal(currentNode, out.mWeighting); } } } @@ -1264,13 +1265,13 @@ void ColladaParser::ReadEffectColor(XmlNode &node, aiColor4D &pColor, Sampler &p // ------------------------------------------------------------------------------------------------ // Reads an effect entry containing a float -void ColladaParser::ReadEffectFloat(XmlNode &node, ai_real &pFloat) { - pFloat = 0.f; +void ColladaParser::ReadEffectFloat(XmlNode &node, ai_real &pReal) { + pReal = 0.f; XmlNode floatNode = node.child("float"); if (floatNode.empty()) { return; } - XmlParser::getValueAsFloat(floatNode, pFloat); + XmlParser::getValueAsReal(floatNode, pReal); } // ------------------------------------------------------------------------------------------------ @@ -2292,9 +2293,9 @@ void ColladaParser::ReadNodeGeometry(XmlNode &node, Node *pNode) { urlMat++; s.mMatName = urlMat; + ReadMaterialVertexInputBinding(instanceMatNode, s); // store the association instance.mMaterials[group] = s; - ReadMaterialVertexInputBinding(instanceMatNode, s); } } } diff --git a/code/AssetLib/FBX/FBXConverter.cpp b/code/AssetLib/FBX/FBXConverter.cpp index cc73756fb..234931cbe 100644 --- a/code/AssetLib/FBX/FBXConverter.cpp +++ b/code/AssetLib/FBX/FBXConverter.cpp @@ -1860,7 +1860,7 @@ aiString FBXConverter::GetTexturePath(const Texture *tex) { // We need to load all textures before referencing them, as FBX file format order may reference a texture before loading it // This may occur on this case too, it has to be studied path.data[0] = '*'; - path.length = 1 + ASSIMP_itoa10(path.data + 1, MAXLEN - 1, index); + path.length = 1 + ASSIMP_itoa10(path.data + 1, AI_MAXLEN - 1, index); } } } @@ -2440,7 +2440,7 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial *out_mat, const PropertyTa // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture) path.data[0] = '*'; - path.length = 1 + ASSIMP_itoa10(path.data + 1, MAXLEN - 1, index); + path.length = 1 + ASSIMP_itoa10(path.data + 1, AI_MAXLEN - 1, index); } out_mat->AddProperty(&path, (name + "|file").c_str(), aiTextureType_UNKNOWN, 0); @@ -2806,7 +2806,7 @@ void FBXConverter::ProcessMorphAnimDatas(std::map auto geoIt = std::find(model->GetGeometry().begin(), model->GetGeometry().end(), geo); auto geoIndex = static_cast(std::distance(model->GetGeometry().begin(), geoIt)); auto name = aiString(FixNodeName(model->Name() + "*")); - name.length = 1 + ASSIMP_itoa10(name.data + name.length, MAXLEN - 1, geoIndex); + name.length = 1 + ASSIMP_itoa10(name.data + name.length, AI_MAXLEN - 1, geoIndex); morphAnimData *animData; auto animIt = morphAnimDatas->find(name.C_Str()); if (animIt == morphAnimDatas->end()) { diff --git a/code/AssetLib/FBX/FBXExporter.cpp b/code/AssetLib/FBX/FBXExporter.cpp index ae210eb1a..bfefc92c9 100644 --- a/code/AssetLib/FBX/FBXExporter.cpp +++ b/code/AssetLib/FBX/FBXExporter.cpp @@ -1051,7 +1051,7 @@ aiNode* get_node_for_mesh(unsigned int meshIndex, aiNode* node) aiMatrix4x4 get_world_transform(const aiNode* node, const aiScene* scene) { std::vector node_chain; - while (node != scene->mRootNode) { + while (node != scene->mRootNode && node != nullptr) { node_chain.push_back(node); node = node->mParent; } @@ -1217,10 +1217,8 @@ void FBXExporter::WriteObjects () } // colors, if any - // TODO only one color channel currently - const int32_t colorChannelIndex = 0; - if (m->HasVertexColors(colorChannelIndex)) { - FBX::Node vertexcolors("LayerElementColor", int32_t(colorChannelIndex)); + for (size_t ci = 0; ci < m->GetNumColorChannels(); ++ci) { + FBX::Node vertexcolors("LayerElementColor", int32_t(ci)); vertexcolors.Begin(outstream, binary, indent); vertexcolors.DumpProperties(outstream, binary, indent); vertexcolors.EndProperties(outstream, binary, indent); @@ -1230,7 +1228,7 @@ void FBXExporter::WriteObjects () "Version", int32_t(101), outstream, binary, indent ); char layerName[8]; - snprintf(layerName, sizeof(layerName), "COLOR_%d", colorChannelIndex); + snprintf(layerName, sizeof(layerName), "COLOR_%d", int32_t(ci)); FBX::Node::WritePropertyNode( "Name", (const char*)layerName, outstream, binary, indent ); @@ -1247,7 +1245,7 @@ void FBXExporter::WriteObjects () for (size_t fi = 0; fi < m->mNumFaces; ++fi) { const aiFace &f = m->mFaces[fi]; for (size_t pvi = 0; pvi < f.mNumIndices; ++pvi) { - const aiColor4D &c = m->mColors[colorChannelIndex][f.mIndices[pvi]]; + const aiColor4D &c = m->mColors[ci][f.mIndices[pvi]]; color_data.push_back(c.r); color_data.push_back(c.g); color_data.push_back(c.b); @@ -1354,11 +1352,14 @@ void FBXExporter::WriteObjects () le.AddChild("Type", "LayerElementNormal"); le.AddChild("TypedIndex", int32_t(0)); layer.AddChild(le); - // TODO only 1 color channel currently - le = FBX::Node("LayerElement"); - le.AddChild("Type", "LayerElementColor"); - le.AddChild("TypedIndex", int32_t(0)); - layer.AddChild(le); + + for (size_t ci = 0; ci < m->GetNumColorChannels(); ++ci) { + le = FBX::Node("LayerElement"); + le.AddChild("Type", "LayerElementColor"); + le.AddChild("TypedIndex", int32_t(ci)); + layer.AddChild(le); + } + le = FBX::Node("LayerElement"); le.AddChild("Type", "LayerElementMaterial"); le.AddChild("TypedIndex", int32_t(0)); @@ -1868,33 +1869,26 @@ void FBXExporter::WriteObjects () // one sticky point is that the number of vertices may not match, // because assimp splits vertices by normal, uv, etc. - // functor for aiNode sorting - struct SortNodeByName - { - bool operator()(const aiNode *lhs, const aiNode *rhs) const - { - return strcmp(lhs->mName.C_Str(), rhs->mName.C_Str()) < 0; - } - }; // first we should mark the skeleton for each mesh. // the skeleton must include not only the aiBones, // but also all their parent nodes. // anything that affects the position of any bone node must be included. - // Use SorNodeByName to make sure the exported result will be the same across all systems - // Otherwise the aiNodes of the skeleton would be sorted based on the pointer address, which isn't consistent - std::vector> skeleton_by_mesh(mScene->mNumMeshes); + + // note that we want to preserve input order as much as possible here. + // previously, sorting by name lead to consistent output across systems, but was not + // suitable for downstream consumption by some applications. + std::vector> skeleton_by_mesh(mScene->mNumMeshes); // at the same time we can build a list of all the skeleton nodes, // which will be used later to mark them as type "limbNode". std::unordered_set limbnodes; //actual bone nodes in fbx, without parenting-up - std::unordered_set setAllBoneNamesInScene; - for(unsigned int m = 0; m < mScene->mNumMeshes; ++ m) - { + std::vector allBoneNames; + for(unsigned int m = 0; m < mScene->mNumMeshes; ++ m) { aiMesh* pMesh = mScene->mMeshes[m]; for(unsigned int b = 0; b < pMesh->mNumBones; ++ b) - setAllBoneNamesInScene.insert(pMesh->mBones[b]->mName.data); + allBoneNames.push_back(pMesh->mBones[b]->mName.data); } aiMatrix4x4 mxTransIdentity; @@ -1902,7 +1896,7 @@ void FBXExporter::WriteObjects () std::map node_by_bone; for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { const aiMesh* m = mScene->mMeshes[mi]; - std::set skeleton; + std::vector skeleton; for (size_t bi =0; bi < m->mNumBones; ++bi) { const aiBone* b = m->mBones[bi]; const std::string name(b->mName.C_Str()); @@ -1921,7 +1915,7 @@ void FBXExporter::WriteObjects () node_by_bone[name] = n; limbnodes.insert(n); } - skeleton.insert(n); + skeleton.push_back(n); // mark all parent nodes as skeleton as well, // up until we find the root node, // or else the node containing the mesh, @@ -1932,7 +1926,7 @@ void FBXExporter::WriteObjects () parent = parent->mParent ) { // if we've already done this node we can skip it all - if (skeleton.count(parent)) { + if (std::find(skeleton.begin(), skeleton.end(), parent) != skeleton.end()) { break; } // ignore fbx transform nodes as these will be collapsed later @@ -1942,7 +1936,7 @@ void FBXExporter::WriteObjects () continue; } //not a bone in scene && no effect in transform - if(setAllBoneNamesInScene.find(node_name)==setAllBoneNamesInScene.end() + if (std::find(allBoneNames.begin(), allBoneNames.end(), node_name) == allBoneNames.end() && parent->mTransformation == mxTransIdentity) { continue; } @@ -2027,7 +2021,7 @@ void FBXExporter::WriteObjects () aiMatrix4x4 mesh_xform = get_world_transform(mesh_node, mScene); // now make a subdeformer for each bone in the skeleton - const std::set skeleton= skeleton_by_mesh[mi]; + const auto & skeleton= skeleton_by_mesh[mi]; for (const aiNode* bone_node : skeleton) { // if there's a bone for this node, find it const aiBone* b = nullptr; diff --git a/code/AssetLib/FBX/FBXMeshGeometry.cpp b/code/AssetLib/FBX/FBXMeshGeometry.cpp index 3b706727a..67488f53a 100644 --- a/code/AssetLib/FBX/FBXMeshGeometry.cpp +++ b/code/AssetLib/FBX/FBXMeshGeometry.cpp @@ -644,10 +644,12 @@ void MeshGeometry::ReadVertexDataMaterials(std::vector& materials_out, cons return; } - // materials are handled separately. First of all, they are assigned per-face - // and not per polyvert. Secondly, ReferenceInformationType=IndexToDirect - // has a slightly different meaning for materials. - ParseVectorDataArray(materials_out,GetRequiredElement(source,"Materials")); + if (source["Materials"]) { + // materials are handled separately. First of all, they are assigned per-face + // and not per polyvert. Secondly, ReferenceInformationType=IndexToDirect + // has a slightly different meaning for materials. + ParseVectorDataArray(materials_out, GetRequiredElement(source, "Materials")); + } if (MappingInformationType == "AllSame") { // easy - same material for all faces diff --git a/code/AssetLib/FBX/FBXParser.h b/code/AssetLib/FBX/FBXParser.h index 63dbb023b..2ca216d8c 100644 --- a/code/AssetLib/FBX/FBXParser.h +++ b/code/AssetLib/FBX/FBXParser.h @@ -63,7 +63,6 @@ class Scope; class Parser; class Element; -// XXX should use C++11's unique_ptr - but assimp's need to keep working with 03 using ScopeList = std::vector; using ElementMap = std::fbx_unordered_multimap< std::string, Element*>; using ElementCollection = std::pair; @@ -135,7 +134,7 @@ public: const char* elementNameCStr = elementName.c_str(); for (auto element = elements.begin(); element != elements.end(); ++element) { - if (!ASSIMP_strincmp(element->first.c_str(), elementNameCStr, MAXLEN)) { + if (!ASSIMP_strincmp(element->first.c_str(), elementNameCStr, AI_MAXLEN)) { return element->second; } } diff --git a/code/AssetLib/FBX/FBXProperties.cpp b/code/AssetLib/FBX/FBXProperties.cpp index df39098fa..6f577d09f 100644 --- a/code/AssetLib/FBX/FBXProperties.cpp +++ b/code/AssetLib/FBX/FBXProperties.cpp @@ -243,7 +243,6 @@ DirectPropertyMap PropertyTable::GetUnparsedProperties() const // Read the element's value. // Wrap the naked pointer (since the call site is required to acquire ownership) - // std::unique_ptr from C++11 would be preferred both as a wrapper and a return value. std::shared_ptr prop = std::shared_ptr(ReadTypedProperty(*currentElement.second)); // Element could not be read. Skip it. diff --git a/code/AssetLib/IFC/IFCLoader.cpp b/code/AssetLib/IFC/IFCLoader.cpp index 9414697df..13ea2d429 100644 --- a/code/AssetLib/IFC/IFCLoader.cpp +++ b/code/AssetLib/IFC/IFCLoader.cpp @@ -220,7 +220,7 @@ void IFCImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy std::unique_ptr db(STEP::ReadFileHeader(std::move(stream))); const STEP::HeaderInfo &head = static_cast(*db).GetHeader(); - if (!head.fileSchema.size() || head.fileSchema.substr(0, 3) != "IFC") { + if (!head.fileSchema.size() || head.fileSchema.substr(0, 4) != "IFC2") { ThrowException("Unrecognized file schema: " + head.fileSchema); } @@ -260,6 +260,8 @@ void IFCImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy ThrowException("missing IfcProject entity"); } + + ConversionData conv(*db, proj->To(), pScene, settings); SetUnits(conv); SetCoordinateSpace(conv); @@ -352,6 +354,11 @@ void ConvertUnit(const ::Assimp::STEP::EXPRESS::DataType &dt, ConversionData &co // ------------------------------------------------------------------------------------------------ void SetUnits(ConversionData &conv) { + if (conv.proj.UnitsInContext == nullptr) { + IFCImporter::LogError("Skipping conversion data, nullptr."); + return; + } + // see if we can determine the coordinate space used to express. for (size_t i = 0; i < conv.proj.UnitsInContext->Units.size(); ++i) { ConvertUnit(*conv.proj.UnitsInContext->Units[i], conv); diff --git a/code/AssetLib/Irr/IRRLoader.cpp b/code/AssetLib/Irr/IRRLoader.cpp index f41a2543d..12ce86260 100644 --- a/code/AssetLib/Irr/IRRLoader.cpp +++ b/code/AssetLib/Irr/IRRLoader.cpp @@ -168,7 +168,7 @@ void IRRImporter::BuildSkybox(std::vector &meshes, std::vectorAddProperty(&s, AI_MATKEY_NAME); int shading = aiShadingMode_NoShading; @@ -316,7 +316,7 @@ void IRRImporter::ComputeAnimations(Node *root, aiNode *real, std::vectormNodeName.length = ::ai_snprintf(anim->mNodeName.data, MAXLEN, + anim->mNodeName.length = ::ai_snprintf(anim->mNodeName.data, AI_MAXLEN, "$INST_DUMMY_%i_%s", total - 1, (root->name.length() ? root->name.c_str() : "")); diff --git a/code/AssetLib/LWS/LWSLoader.cpp b/code/AssetLib/LWS/LWSLoader.cpp index 4c3a44785..047ab0cc9 100644 --- a/code/AssetLib/LWS/LWSLoader.cpp +++ b/code/AssetLib/LWS/LWSLoader.cpp @@ -305,14 +305,14 @@ void LWSImporter::SetupNodeName(aiNode *nd, LWS::NodeDesc &src) { } std::string::size_type t = src.path.substr(s).find_last_of('.'); - nd->mName.length = ::ai_snprintf(nd->mName.data, MAXLEN, "%s_(%08X)", src.path.substr(s).substr(0, t).c_str(), combined); - if (nd->mName.length > MAXLEN) { - nd->mName.length = MAXLEN; + nd->mName.length = ::ai_snprintf(nd->mName.data, AI_MAXLEN, "%s_(%08X)", src.path.substr(s).substr(0, t).c_str(), combined); + if (nd->mName.length > AI_MAXLEN) { + nd->mName.length = AI_MAXLEN; } return; } } - nd->mName.length = ::ai_snprintf(nd->mName.data, MAXLEN, "%s_(%08X)", src.name, combined); + nd->mName.length = ::ai_snprintf(nd->mName.data, AI_MAXLEN, "%s_(%08X)", src.name, combined); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/MD5/MD5Loader.cpp b/code/AssetLib/MD5/MD5Loader.cpp index 0976484a4..02f08d3ea 100644 --- a/code/AssetLib/MD5/MD5Loader.cpp +++ b/code/AssetLib/MD5/MD5Loader.cpp @@ -707,7 +707,7 @@ void MD5Importer::LoadMD5CameraFile() { for (std::vector::const_iterator it = cuts.begin(); it != cuts.end() - 1; ++it) { aiAnimation *anim = *tmp++ = new aiAnimation(); - anim->mName.length = ::ai_snprintf(anim->mName.data, MAXLEN, "anim%u_from_%u_to_%u", (unsigned int)(it - cuts.begin()), (*it), *(it + 1)); + anim->mName.length = ::ai_snprintf(anim->mName.data, AI_MAXLEN, "anim%u_from_%u_to_%u", (unsigned int)(it - cuts.begin()), (*it), *(it + 1)); anim->mTicksPerSecond = cameraParser.fFrameRate; anim->mChannels = new aiNodeAnim *[anim->mNumChannels = 1]; diff --git a/code/AssetLib/MD5/MD5Parser.cpp b/code/AssetLib/MD5/MD5Parser.cpp index 24882af7e..2de8d5033 100644 --- a/code/AssetLib/MD5/MD5Parser.cpp +++ b/code/AssetLib/MD5/MD5Parser.cpp @@ -234,8 +234,12 @@ inline void AI_MD5_READ_TRIPLE(aiVector3D &vec, const char **sz, const char *buf AI_MD5_SKIP_SPACES(sz, bufferEnd, linenumber); if ('(' != **sz) { MD5Parser::ReportWarning("Unexpected token: ( was expected", linenumber); + if (*sz == bufferEnd) + return; ++*sz; } + if (*sz == bufferEnd) + return; ++*sz; AI_MD5_SKIP_SPACES(sz, bufferEnd, linenumber); *sz = fast_atoreal_move(*sz, (float &)vec.x); @@ -247,6 +251,8 @@ inline void AI_MD5_READ_TRIPLE(aiVector3D &vec, const char **sz, const char *buf if (')' != **sz) { MD5Parser::ReportWarning("Unexpected token: ) was expected", linenumber); } + if (*sz == bufferEnd) + return; ++*sz; } diff --git a/code/AssetLib/MDL/HalfLife/HL1ImportSettings.h b/code/AssetLib/MDL/HalfLife/HL1ImportSettings.h index 52f98cb58..7c2fa2ff2 100644 --- a/code/AssetLib/MDL/HalfLife/HL1ImportSettings.h +++ b/code/AssetLib/MDL/HalfLife/HL1ImportSettings.h @@ -63,7 +63,8 @@ struct HL1ImportSettings { read_bone_controllers(false), read_hitboxes(false), read_textures(false), - read_misc_global_info(false) { + read_misc_global_info(false), + transform_coord_system(true) { } bool read_animations; @@ -76,6 +77,7 @@ struct HL1ImportSettings { bool read_hitboxes; bool read_textures; bool read_misc_global_info; + bool transform_coord_system; }; } // namespace HalfLife diff --git a/code/AssetLib/MDL/MDLLoader.cpp b/code/AssetLib/MDL/MDLLoader.cpp index 99b0145af..40725b1f7 100644 --- a/code/AssetLib/MDL/MDLLoader.cpp +++ b/code/AssetLib/MDL/MDLLoader.cpp @@ -99,7 +99,7 @@ MDLImporter::MDLImporter() : // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. bool MDLImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { - static const uint32_t tokens[] = { + static constexpr uint32_t tokens[] = { AI_MDL_MAGIC_NUMBER_LE_HL2a, AI_MDL_MAGIC_NUMBER_LE_HL2b, AI_MDL_MAGIC_NUMBER_LE_GS7, @@ -138,6 +138,7 @@ void MDLImporter::SetupProperties(const Importer *pImp) { mHL1ImportSettings.read_bone_controllers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_MDL_HL1_READ_BONE_CONTROLLERS, true); mHL1ImportSettings.read_hitboxes = pImp->GetPropertyBool(AI_CONFIG_IMPORT_MDL_HL1_READ_HITBOXES, true); mHL1ImportSettings.read_misc_global_info = pImp->GetPropertyBool(AI_CONFIG_IMPORT_MDL_HL1_READ_MISC_GLOBAL_INFO, true); + mHL1ImportSettings.transform_coord_system = pImp->GetPropertyBool(AI_CONFIG_IMPORT_MDL_HL1_TRANSFORM_COORD_SYSTEM); } // ------------------------------------------------------------------------------------------------ @@ -146,6 +147,20 @@ const aiImporterDesc *MDLImporter::GetInfo() const { return &desc; } +// ------------------------------------------------------------------------------------------------ +static void transformCoordinateSystem(const aiScene *pScene) { + if (pScene == nullptr) { + return; + } + + pScene->mRootNode->mTransformation = aiMatrix4x4( + 0.f, -1.f, 0.f, 0.f, + 0.f, 0.f, 1.f, 0.f, + -1.f, 0.f, 0.f, 0.f, + 0.f, 0.f, 0.f, 1.f + ); +} + // ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. void MDLImporter::InternReadFile(const std::string &pFile, @@ -246,18 +261,16 @@ void MDLImporter::InternReadFile(const std::string &pFile, ". Magic word (", ai_str_toprintable((const char *)&iMagicWord, sizeof(iMagicWord)), ") is not known"); } - if (is_half_life){ + if (is_half_life && mHL1ImportSettings.transform_coord_system) { // Now rotate the whole scene 90 degrees around the z and x axes to convert to internal coordinate system - pScene->mRootNode->mTransformation = aiMatrix4x4( - 0.f, -1.f, 0.f, 0.f, - 0.f, 0.f, 1.f, 0.f, - -1.f, 0.f, 0.f, 0.f, - 0.f, 0.f, 0.f, 1.f); - } - else { + transformCoordinateSystem(pScene); + } else { // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system - pScene->mRootNode->mTransformation = aiMatrix4x4(1.f, 0.f, 0.f, 0.f, - 0.f, 0.f, 1.f, 0.f, 0.f, -1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f); + pScene->mRootNode->mTransformation = aiMatrix4x4( + 1.f, 0.f, 0.f, 0.f, + 0.f, 0.f, 1.f, 0.f, + 0.f, -1.f, 0.f, 0.f, + 0.f, 0.f, 0.f, 1.f); } DeleteBufferAndCleanup(); @@ -962,7 +975,7 @@ void MDLImporter::CalcAbsBoneMatrices_3DGS_MDL7(MDL::IntBone_MDL7 **apcOutBones) if (AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_NOT_THERE == pcHeader->bone_stc_size) { // no real name for our poor bone is specified :-( - pcOutBone->mName.length = ai_snprintf(pcOutBone->mName.data, MAXLEN, + pcOutBone->mName.length = ai_snprintf(pcOutBone->mName.data, AI_MAXLEN, "UnnamedBone_%i", iBone); } else { // Make sure we won't run over the buffer's end if there is no @@ -1567,7 +1580,7 @@ void MDLImporter::InternReadFile_3DGS_MDL7() { } else { pcNode->mName.length = (ai_uint32)::strlen(szBuffer); } - ::strncpy(pcNode->mName.data, szBuffer, MAXLEN - 1); + ::strncpy(pcNode->mName.data, szBuffer, AI_MAXLEN - 1); ++p; } } diff --git a/code/AssetLib/MDL/MDLMaterialLoader.cpp b/code/AssetLib/MDL/MDLMaterialLoader.cpp index 38c42c1a5..d0a2d5f79 100644 --- a/code/AssetLib/MDL/MDLMaterialLoader.cpp +++ b/code/AssetLib/MDL/MDLMaterialLoader.cpp @@ -494,7 +494,7 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7( aiString szFile; const size_t iLen = strlen((const char *)szCurrent); - size_t iLen2 = iLen > (MAXLEN - 1) ? (MAXLEN - 1) : iLen; + size_t iLen2 = iLen > (AI_MAXLEN - 1) ? (AI_MAXLEN - 1) : iLen; memcpy(szFile.data, (const char *)szCurrent, iLen2); szFile.data[iLen2] = '\0'; szFile.length = static_cast(iLen2); @@ -610,7 +610,7 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7( if (is_not_qnan(clrTexture.r)) { clrTemp.r *= clrTexture.a; } - pcMatOut->AddProperty(&clrTemp.r, 1, AI_MATKEY_OPACITY); + pcMatOut->AddProperty(&clrTemp.r, 1, AI_MATKEY_OPACITY); // read phong power int iShadingMode = (int)aiShadingMode_Gouraud; @@ -730,9 +730,12 @@ void MDLImporter::SkipSkinLump_3DGS_MDL7( // if an ASCII effect description (HLSL?) is contained in the file, // we can simply ignore it ... if (iType & AI_MDL7_SKINTYPE_MATERIAL_ASCDEF) { - int32_t iMe = *((int32_t *)szCurrent); + VALIDATE_FILE_SIZE(szCurrent + sizeof(int32_t)); + int32_t iMe = 0; + ::memcpy(&iMe, szCurrent, sizeof(int32_t)); AI_SWAP4(iMe); szCurrent += sizeof(char) * iMe + sizeof(int32_t); + VALIDATE_FILE_SIZE(szCurrent); } *szCurrentOut = szCurrent; } diff --git a/code/AssetLib/OFF/OFFLoader.cpp b/code/AssetLib/OFF/OFFLoader.cpp index 566e3212e..a3feaa53c 100644 --- a/code/AssetLib/OFF/OFFLoader.cpp +++ b/code/AssetLib/OFF/OFFLoader.cpp @@ -316,7 +316,7 @@ void OFFImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials]; aiMaterial *pcMat = new aiMaterial(); - aiColor4D clr(ai_real(0.6), ai_real(0.6), ai_real(0.6), ai_real(1.0)); + aiColor4D clr(0.6f, 0.6f, 0.6f, 1.0f); pcMat->AddProperty(&clr, 1, AI_MATKEY_COLOR_DIFFUSE); pScene->mMaterials[0] = pcMat; diff --git a/code/AssetLib/Obj/ObjFileData.h b/code/AssetLib/Obj/ObjFileData.h index 3dc20c799..205c855e5 100644 --- a/code/AssetLib/Obj/ObjFileData.h +++ b/code/AssetLib/Obj/ObjFileData.h @@ -199,12 +199,12 @@ struct Material { //! Constructor Material() : - diffuse(ai_real(0.6), ai_real(0.6), ai_real(0.6)), + diffuse(0.6f, 0.6f, 0.6f), alpha(ai_real(1.0)), shineness(ai_real(0.0)), illumination_model(1), ior(ai_real(1.0)), - transparent(ai_real(1.0), ai_real(1.0), ai_real(1.0)), + transparent(1.0f, 1.0, 1.0), roughness(), metallic(), sheen(), diff --git a/code/AssetLib/Obj/ObjFileImporter.cpp b/code/AssetLib/Obj/ObjFileImporter.cpp index e956f83d4..09aa84c22 100644 --- a/code/AssetLib/Obj/ObjFileImporter.cpp +++ b/code/AssetLib/Obj/ObjFileImporter.cpp @@ -193,7 +193,7 @@ void ObjFileImporter::CreateDataFromImport(const ObjFile::Model *pModel, aiScene pScene->mRootNode->mChildren = new aiNode *[childCount]; // Create nodes for the whole scene - std::vector MeshArray; + std::vector> MeshArray; MeshArray.reserve(meshCount); for (size_t index = 0; index < pModel->mObjects.size(); ++index) { createNodes(pModel, pModel->mObjects[index], pScene->mRootNode, pScene, MeshArray); @@ -205,7 +205,7 @@ void ObjFileImporter::CreateDataFromImport(const ObjFile::Model *pModel, aiScene if (pScene->mNumMeshes > 0) { pScene->mMeshes = new aiMesh *[MeshArray.size()]; for (size_t index = 0; index < MeshArray.size(); ++index) { - pScene->mMeshes[index] = MeshArray[index]; + pScene->mMeshes[index] = MeshArray[index].release(); } } @@ -257,7 +257,7 @@ void ObjFileImporter::CreateDataFromImport(const ObjFile::Model *pModel, aiScene // Creates all nodes of the model aiNode *ObjFileImporter::createNodes(const ObjFile::Model *pModel, const ObjFile::Object *pObject, aiNode *pParent, aiScene *pScene, - std::vector &MeshArray) { + std::vector> &MeshArray) { ai_assert(nullptr != pModel); if (nullptr == pObject) { return nullptr; @@ -275,12 +275,10 @@ aiNode *ObjFileImporter::createNodes(const ObjFile::Model *pModel, const ObjFile for (size_t i = 0; i < pObject->m_Meshes.size(); ++i) { unsigned int meshId = pObject->m_Meshes[i]; - aiMesh *pMesh = createTopology(pModel, pObject, meshId); + std::unique_ptr pMesh = createTopology(pModel, pObject, meshId); if (pMesh != nullptr) { if (pMesh->mNumFaces > 0) { - MeshArray.push_back(pMesh); - } else { - delete pMesh; + MeshArray.push_back(std::move(pMesh)); } } } @@ -312,7 +310,7 @@ aiNode *ObjFileImporter::createNodes(const ObjFile::Model *pModel, const ObjFile // ------------------------------------------------------------------------------------------------ // Create topology data -aiMesh *ObjFileImporter::createTopology(const ObjFile::Model *pModel, const ObjFile::Object *pData, unsigned int meshIndex) { +std::unique_ptr ObjFileImporter::createTopology(const ObjFile::Model *pModel, const ObjFile::Object *pData, unsigned int meshIndex) { // Checking preconditions ai_assert(nullptr != pModel); @@ -394,7 +392,7 @@ aiMesh *ObjFileImporter::createTopology(const ObjFile::Model *pModel, const ObjF // Create mesh vertices createVertexArray(pModel, pData, meshIndex, pMesh.get(), uiIdxCount); - return pMesh.release(); + return pMesh; } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/Obj/ObjFileImporter.h b/code/AssetLib/Obj/ObjFileImporter.h index e76c27950..6768013e4 100644 --- a/code/AssetLib/Obj/ObjFileImporter.h +++ b/code/AssetLib/Obj/ObjFileImporter.h @@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include #include struct aiMesh; @@ -84,10 +85,10 @@ protected: //! \brief Creates all nodes stored in imported content. aiNode *createNodes(const ObjFile::Model *pModel, const ObjFile::Object *pData, - aiNode *pParent, aiScene *pScene, std::vector &MeshArray); + aiNode *pParent, aiScene *pScene, std::vector> &MeshArray); //! \brief Creates topology data like faces and meshes for the geometry. - aiMesh *createTopology(const ObjFile::Model *pModel, const ObjFile::Object *pData, + std::unique_ptr createTopology(const ObjFile::Model *pModel, const ObjFile::Object *pData, unsigned int uiMeshIndex); //! \brief Creates vertices from model. diff --git a/code/AssetLib/Obj/ObjFileMtlImporter.cpp b/code/AssetLib/Obj/ObjFileMtlImporter.cpp index f8e3e1cde..effdf627f 100644 --- a/code/AssetLib/Obj/ObjFileMtlImporter.cpp +++ b/code/AssetLib/Obj/ObjFileMtlImporter.cpp @@ -343,7 +343,7 @@ void ObjFileMtlImporter::createMaterial() { } } - name = trim_whitespaces(name); + name = ai_trim(name); std::map::iterator it = m_pModel->mMaterialMap.find(name); if (m_pModel->mMaterialMap.end() == it) { diff --git a/code/AssetLib/Obj/ObjFileParser.cpp b/code/AssetLib/Obj/ObjFileParser.cpp index c0f1fc483..fec1fe87b 100644 --- a/code/AssetLib/Obj/ObjFileParser.cpp +++ b/code/AssetLib/Obj/ObjFileParser.cpp @@ -577,15 +577,17 @@ void ObjFileParser::getMaterialDesc() { // Get name std::string strName(pStart, &(*m_DataIt)); - strName = trim_whitespaces(strName); + strName = ai_trim(strName); if (strName.empty()) { skip = true; } - // If the current mesh has the same material, we simply ignore that 'usemtl' command + // If the current mesh has the same material, we will ignore that 'usemtl' command // There is no need to create another object or even mesh here - if (m_pModel->mCurrentMaterial && m_pModel->mCurrentMaterial->MaterialName == aiString(strName)) { - skip = true; + if (!skip) { + if (m_pModel->mCurrentMaterial && m_pModel->mCurrentMaterial->MaterialName == aiString(strName)) { + skip = true; + } } if (!skip) { diff --git a/code/AssetLib/Obj/ObjTools.h b/code/AssetLib/Obj/ObjTools.h index 664402ee3..ac5e119f2 100644 --- a/code/AssetLib/Obj/ObjTools.h +++ b/code/AssetLib/Obj/ObjTools.h @@ -247,22 +247,6 @@ inline char_t getFloat(char_t it, char_t end, ai_real &value) { return it; } -/** - * @brief Will remove white-spaces for a string. - * @param[in] str The string to clean - * @return The trimmed string. - */ -template -inline string_type trim_whitespaces(string_type str) { - while (!str.empty() && IsSpace(str[0])) { - str.erase(0); - } - while (!str.empty() && IsSpace(str[str.length() - 1])) { - str.erase(str.length() - 1); - } - return str; -} - /** * @brief Checks for a line-end. * @param[in] it Current iterator in string. diff --git a/code/AssetLib/Ply/PlyExporter.cpp b/code/AssetLib/Ply/PlyExporter.cpp index 2dc3311ff..9c4b281b8 100644 --- a/code/AssetLib/Ply/PlyExporter.cpp +++ b/code/AssetLib/Ply/PlyExporter.cpp @@ -346,10 +346,22 @@ void PlyExporter::WriteMeshVertsBinary(const aiMesh* m, unsigned int components) for (unsigned int n = PLY_EXPORT_HAS_COLORS, c = 0; (components & n) && c != AI_MAX_NUMBER_OF_COLOR_SETS; n <<= 1, ++c) { if (m->HasVertexColors(c)) { - mOutput.write(reinterpret_cast(&m->mColors[c][i].r), 16); + unsigned char rgba[4] = { + static_cast(m->mColors[c][i].r * 255), + static_cast(m->mColors[c][i].g * 255), + static_cast(m->mColors[c][i].b * 255), + static_cast(m->mColors[c][i].a * 255) + }; + mOutput.write(reinterpret_cast(&rgba), 4); } else { - mOutput.write(reinterpret_cast(&defaultColor.r), 16); + unsigned char rgba[4] = { + static_cast(defaultColor.r * 255), + static_cast(defaultColor.g * 255), + static_cast(defaultColor.b * 255), + static_cast(defaultColor.a * 255) + }; + mOutput.write(reinterpret_cast(&rgba), 4); } } diff --git a/code/AssetLib/Ply/PlyLoader.cpp b/code/AssetLib/Ply/PlyLoader.cpp index 3e92339fb..b211818df 100644 --- a/code/AssetLib/Ply/PlyLoader.cpp +++ b/code/AssetLib/Ply/PlyLoader.cpp @@ -81,6 +81,27 @@ namespace { return props[idx]; } + + // ------------------------------------------------------------------------------------------------ + static bool isBigEndian(const char *szMe) { + ai_assert(nullptr != szMe); + + // binary_little_endian + // binary_big_endian + bool isBigEndian{ false }; +#if (defined AI_BUILD_BIG_ENDIAN) + if ('l' == *szMe || 'L' == *szMe) { + isBigEndian = true; + } +#else + if ('b' == *szMe || 'B' == *szMe) { + isBigEndian = true; + } +#endif // ! AI_BUILD_BIG_ENDIAN + + return isBigEndian; + } + } // namespace // ------------------------------------------------------------------------------------------------ @@ -92,6 +113,11 @@ PLYImporter::PLYImporter() : // empty } +// ------------------------------------------------------------------------------------------------ +PLYImporter::~PLYImporter() { + delete mGeneratedMesh; +} + // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. bool PLYImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { @@ -104,26 +130,6 @@ const aiImporterDesc *PLYImporter::GetInfo() const { return &desc; } -// ------------------------------------------------------------------------------------------------ -static bool isBigEndian(const char *szMe) { - ai_assert(nullptr != szMe); - - // binary_little_endian - // binary_big_endian - bool isBigEndian(false); -#if (defined AI_BUILD_BIG_ENDIAN) - if ('l' == *szMe || 'L' == *szMe) { - isBigEndian = true; - } -#else - if ('b' == *szMe || 'B' == *szMe) { - isBigEndian = true; - } -#endif // ! AI_BUILD_BIG_ENDIAN - - return isBigEndian; -} - // ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. void PLYImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { @@ -134,7 +140,7 @@ void PLYImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy } // Get the file-size - const size_t fileSize(fileStream->FileSize()); + const size_t fileSize = fileStream->FileSize(); if (0 == fileSize) { throw DeadlyImportError("File ", pFile, " is empty."); } @@ -180,7 +186,7 @@ void PLYImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy } } else if (!::strncmp(szMe, "binary_", 7)) { szMe += 7; - const bool bIsBE(isBigEndian(szMe)); + const bool bIsBE = isBigEndian(szMe); // skip the line, parse the rest of the header and build the DOM if (!PLY::DOM::ParseInstanceBinary(streamedBuffer, &sPlyDom, this, bIsBE)) { @@ -241,7 +247,9 @@ void PLYImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy // fill the mesh list pScene->mNumMeshes = 1; pScene->mMeshes = new aiMesh *[pScene->mNumMeshes]; - pScene->mMeshes[0] = mGeneratedMesh; + pScene->mMeshes[0] = mGeneratedMesh; + + // Move the mesh ownership into the scene instance mGeneratedMesh = nullptr; // generate a simple node structure @@ -254,20 +262,22 @@ void PLYImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy } } +static constexpr ai_uint NotSet = 0xFFFFFFFF; + void PLYImporter::LoadVertex(const PLY::Element *pcElement, const PLY::ElementInstance *instElement, unsigned int pos) { ai_assert(nullptr != pcElement); ai_assert(nullptr != instElement); - ai_uint aiPositions[3] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + ai_uint aiPositions[3] = { NotSet, NotSet, NotSet }; PLY::EDataType aiTypes[3] = { EDT_Char, EDT_Char, EDT_Char }; - ai_uint aiNormal[3] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + ai_uint aiNormal[3] = { NotSet, NotSet, NotSet }; PLY::EDataType aiNormalTypes[3] = { EDT_Char, EDT_Char, EDT_Char }; - unsigned int aiColors[4] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + unsigned int aiColors[4] = { NotSet, NotSet, NotSet, NotSet }; PLY::EDataType aiColorsTypes[4] = { EDT_Char, EDT_Char, EDT_Char, EDT_Char }; - unsigned int aiTexcoord[2] = { 0xFFFFFFFF, 0xFFFFFFFF }; + unsigned int aiTexcoord[2] = { NotSet, NotSet }; PLY::EDataType aiTexcoordTypes[2] = { EDT_Char, EDT_Char }; // now check whether which normal components are available @@ -337,17 +347,17 @@ void PLYImporter::LoadVertex(const PLY::Element *pcElement, const PLY::ElementIn if (0 != cnt) { // Position aiVector3D vOut; - if (0xFFFFFFFF != aiPositions[0]) { + if (NotSet != aiPositions[0]) { vOut.x = PLY::PropertyInstance::ConvertTo( GetProperty(instElement->alProperties, aiPositions[0]).avList.front(), aiTypes[0]); } - if (0xFFFFFFFF != aiPositions[1]) { + if (NotSet != aiPositions[1]) { vOut.y = PLY::PropertyInstance::ConvertTo( GetProperty(instElement->alProperties, aiPositions[1]).avList.front(), aiTypes[1]); } - if (0xFFFFFFFF != aiPositions[2]) { + if (NotSet != aiPositions[2]) { vOut.z = PLY::PropertyInstance::ConvertTo( GetProperty(instElement->alProperties, aiPositions[2]).avList.front(), aiTypes[2]); } @@ -355,19 +365,19 @@ void PLYImporter::LoadVertex(const PLY::Element *pcElement, const PLY::ElementIn // Normals aiVector3D nOut; bool haveNormal = false; - if (0xFFFFFFFF != aiNormal[0]) { + if (NotSet != aiNormal[0]) { nOut.x = PLY::PropertyInstance::ConvertTo( GetProperty(instElement->alProperties, aiNormal[0]).avList.front(), aiNormalTypes[0]); haveNormal = true; } - if (0xFFFFFFFF != aiNormal[1]) { + if (NotSet != aiNormal[1]) { nOut.y = PLY::PropertyInstance::ConvertTo( GetProperty(instElement->alProperties, aiNormal[1]).avList.front(), aiNormalTypes[1]); haveNormal = true; } - if (0xFFFFFFFF != aiNormal[2]) { + if (NotSet != aiNormal[2]) { nOut.z = PLY::PropertyInstance::ConvertTo( GetProperty(instElement->alProperties, aiNormal[2]).avList.front(), aiNormalTypes[2]); haveNormal = true; @@ -376,7 +386,7 @@ void PLYImporter::LoadVertex(const PLY::Element *pcElement, const PLY::ElementIn // Colors aiColor4D cOut; bool haveColor = false; - if (0xFFFFFFFF != aiColors[0]) { + if (NotSet != aiColors[0]) { cOut.r = NormalizeColorValue(GetProperty(instElement->alProperties, aiColors[0]) .avList.front(), @@ -384,7 +394,7 @@ void PLYImporter::LoadVertex(const PLY::Element *pcElement, const PLY::ElementIn haveColor = true; } - if (0xFFFFFFFF != aiColors[1]) { + if (NotSet != aiColors[1]) { cOut.g = NormalizeColorValue(GetProperty(instElement->alProperties, aiColors[1]) .avList.front(), @@ -392,7 +402,7 @@ void PLYImporter::LoadVertex(const PLY::Element *pcElement, const PLY::ElementIn haveColor = true; } - if (0xFFFFFFFF != aiColors[2]) { + if (NotSet != aiColors[2]) { cOut.b = NormalizeColorValue(GetProperty(instElement->alProperties, aiColors[2]) .avList.front(), @@ -401,7 +411,7 @@ void PLYImporter::LoadVertex(const PLY::Element *pcElement, const PLY::ElementIn } // assume 1.0 for the alpha channel if it is not set - if (0xFFFFFFFF == aiColors[3]) { + if (NotSet == aiColors[3]) { cOut.a = 1.0; } else { cOut.a = NormalizeColorValue(GetProperty(instElement->alProperties, @@ -416,13 +426,13 @@ void PLYImporter::LoadVertex(const PLY::Element *pcElement, const PLY::ElementIn aiVector3D tOut; tOut.z = 0; bool haveTextureCoords = false; - if (0xFFFFFFFF != aiTexcoord[0]) { + if (NotSet != aiTexcoord[0]) { tOut.x = PLY::PropertyInstance::ConvertTo( GetProperty(instElement->alProperties, aiTexcoord[0]).avList.front(), aiTexcoordTypes[0]); haveTextureCoords = true; } - if (0xFFFFFFFF != aiTexcoord[1]) { + if (NotSet != aiTexcoord[1]) { tOut.y = PLY::PropertyInstance::ConvertTo( GetProperty(instElement->alProperties, aiTexcoord[1]).avList.front(), aiTexcoordTypes[1]); haveTextureCoords = true; @@ -504,16 +514,12 @@ void PLYImporter::LoadFace(const PLY::Element *pcElement, const PLY::ElementInst bool bOne = false; // index of the vertex index list - unsigned int iProperty = 0xFFFFFFFF; + unsigned int iProperty = NotSet; PLY::EDataType eType = EDT_Char; bool bIsTriStrip = false; - // index of the material index property - // unsigned int iMaterialIndex = 0xFFFFFFFF; - // PLY::EDataType eType2 = EDT_Char; - // texture coordinates - unsigned int iTextureCoord = 0xFFFFFFFF; + unsigned int iTextureCoord = NotSet; PLY::EDataType eType3 = EDT_Char; // face = unique number of vertex indices @@ -564,11 +570,15 @@ void PLYImporter::LoadFace(const PLY::Element *pcElement, const PLY::ElementInst if (mGeneratedMesh->mFaces == nullptr) { mGeneratedMesh->mNumFaces = pcElement->NumOccur; mGeneratedMesh->mFaces = new aiFace[mGeneratedMesh->mNumFaces]; + } else { + if (mGeneratedMesh->mNumFaces < pcElement->NumOccur) { + throw DeadlyImportError("Invalid .ply file: Too many faces"); + } } if (!bIsTriStrip) { // parse the list of vertex indices - if (0xFFFFFFFF != iProperty) { + if (NotSet != iProperty) { const unsigned int iNum = (unsigned int)GetProperty(instElement->alProperties, iProperty).avList.size(); mGeneratedMesh->mFaces[pos].mNumIndices = iNum; mGeneratedMesh->mFaces[pos].mIndices = new unsigned int[iNum]; @@ -581,15 +591,7 @@ void PLYImporter::LoadFace(const PLY::Element *pcElement, const PLY::ElementInst } } - // parse the material index - // cannot be handled without processing the whole file first - /*if (0xFFFFFFFF != iMaterialIndex) - { - mGeneratedMesh->mFaces[pos]. = PLY::PropertyInstance::ConvertTo( - GetProperty(instElement->alProperties, iMaterialIndex).avList.front(), eType2); - }*/ - - if (0xFFFFFFFF != iTextureCoord) { + if (NotSet != iTextureCoord) { const unsigned int iNum = (unsigned int)GetProperty(instElement->alProperties, iTextureCoord).avList.size(); // should be 6 coords @@ -675,41 +677,29 @@ void PLYImporter::GetMaterialColor(const std::vector &avL aiColor4D *clrOut) { ai_assert(nullptr != clrOut); - if (0xFFFFFFFF == aiPositions[0]) + if (NotSet == aiPositions[0]) { clrOut->r = 0.0f; - else { - clrOut->r = NormalizeColorValue(GetProperty(avList, - aiPositions[0]) - .avList.front(), - aiTypes[0]); + } else { + clrOut->r = NormalizeColorValue(GetProperty(avList, aiPositions[0]).avList.front(), aiTypes[0]); } - if (0xFFFFFFFF == aiPositions[1]) + if (NotSet == aiPositions[1]) { clrOut->g = 0.0f; - else { - clrOut->g = NormalizeColorValue(GetProperty(avList, - aiPositions[1]) - .avList.front(), - aiTypes[1]); + } else { + clrOut->g = NormalizeColorValue(GetProperty(avList, aiPositions[1]).avList.front(), aiTypes[1]); } - if (0xFFFFFFFF == aiPositions[2]) + if (NotSet == aiPositions[2]) clrOut->b = 0.0f; else { - clrOut->b = NormalizeColorValue(GetProperty(avList, - aiPositions[2]) - .avList.front(), - aiTypes[2]); + clrOut->b = NormalizeColorValue(GetProperty(avList, aiPositions[2]).avList.front(), aiTypes[2]); } // assume 1.0 for the alpha channel ifit is not set - if (0xFFFFFFFF == aiPositions[3]) + if (NotSet == aiPositions[3]) clrOut->a = 1.0f; else { - clrOut->a = NormalizeColorValue(GetProperty(avList, - aiPositions[3]) - .avList.front(), - aiTypes[3]); + clrOut->a = NormalizeColorValue(GetProperty(avList, aiPositions[3]).avList.front(), aiTypes[3]); } } diff --git a/code/AssetLib/Ply/PlyLoader.h b/code/AssetLib/Ply/PlyLoader.h index 048f40e3f..bc9f276af 100644 --- a/code/AssetLib/Ply/PlyLoader.h +++ b/code/AssetLib/Ply/PlyLoader.h @@ -62,10 +62,10 @@ using namespace PLY; // --------------------------------------------------------------------------- /** Importer class to load the stanford PLY file format */ -class PLYImporter : public BaseImporter { +class PLYImporter final : public BaseImporter { public: PLYImporter(); - ~PLYImporter() override = default; + ~PLYImporter() override; // ------------------------------------------------------------------- /** Returns whether the class can handle the format of the given file. @@ -120,13 +120,9 @@ protected: PLY::PropertyInstance::ValueUnion val, PLY::EDataType eType); - /** Buffer to hold the loaded file */ +private: unsigned char *mBuffer; - - /** Document object model representation extracted from the file */ PLY::DOM *pcDOM; - - /** Mesh generated by loader */ aiMesh *mGeneratedMesh; }; diff --git a/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp b/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp index d2ab59eb4..920279fce 100644 --- a/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp +++ b/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp @@ -588,7 +588,7 @@ bool Q3BSPFileImporter::importTextureFromArchive(const Q3BSP::Q3BSPModel *model, aiString name; name.data[0] = '*'; - name.length = 1 + ASSIMP_itoa10(name.data + 1, static_cast(MAXLEN - 1), static_cast(mTextures.size())); + name.length = 1 + ASSIMP_itoa10(name.data + 1, static_cast(AI_MAXLEN - 1), static_cast(mTextures.size())); archive->Close(pTextureStream); @@ -641,7 +641,7 @@ bool Q3BSPFileImporter::importLightmap(const Q3BSP::Q3BSPModel *pModel, aiScene aiString name; name.data[0] = '*'; - name.length = 1 + ASSIMP_itoa10(name.data + 1, static_cast(MAXLEN - 1), static_cast(mTextures.size())); + name.length = 1 + ASSIMP_itoa10(name.data + 1, static_cast(AI_MAXLEN - 1), static_cast(mTextures.size())); pMatHelper->AddProperty(&name, AI_MATKEY_TEXTURE_LIGHTMAP(1)); mTextures.push_back(pTexture); diff --git a/code/AssetLib/Q3D/Q3DLoader.cpp b/code/AssetLib/Q3D/Q3DLoader.cpp index 435b92e82..84a508979 100644 --- a/code/AssetLib/Q3D/Q3DLoader.cpp +++ b/code/AssetLib/Q3D/Q3DLoader.cpp @@ -250,6 +250,10 @@ void Q3DImporter::InternReadFile(const std::string &pFile, c = stream.GetI1(); while (c) { mat.name.data[mat.name.length++] = c; + if (mat.name.length == AI_MAXLEN) { + ASSIMP_LOG_ERROR("String ouverflow detected, skipped material name parsing."); + break; + } c = stream.GetI1(); } diff --git a/code/AssetLib/SMD/SMDLoader.cpp b/code/AssetLib/SMD/SMDLoader.cpp index df598e5c7..1eac5d934 100644 --- a/code/AssetLib/SMD/SMDLoader.cpp +++ b/code/AssetLib/SMD/SMDLoader.cpp @@ -589,12 +589,12 @@ void SMDImporter::CreateOutputMaterials() { pScene->mMaterials[iMat] = pcMat; aiString szName; - szName.length = static_cast(ai_snprintf(szName.data,MAXLEN,"Texture_%u",iMat)); + szName.length = static_cast(ai_snprintf(szName.data, AI_MAXLEN, "Texture_%u", iMat)); pcMat->AddProperty(&szName,AI_MATKEY_NAME); if (aszTextures[iMat].length()) { - ::strncpy(szName.data, aszTextures[iMat].c_str(),MAXLEN-1); + ::strncpy(szName.data, aszTextures[iMat].c_str(), AI_MAXLEN - 1); szName.length = static_cast( aszTextures[iMat].length() ); pcMat->AddProperty(&szName,AI_MATKEY_TEXTURE_DIFFUSE(0)); } diff --git a/code/AssetLib/STL/STLLoader.cpp b/code/AssetLib/STL/STLLoader.cpp index 38cd2a0b8..90c504d0d 100644 --- a/code/AssetLib/STL/STLLoader.cpp +++ b/code/AssetLib/STL/STLLoader.cpp @@ -181,7 +181,7 @@ void STLImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy mBuffer = &buffer2[0]; // the default vertex color is light gray. - mClrColorDefault.r = mClrColorDefault.g = mClrColorDefault.b = mClrColorDefault.a = (ai_real)0.6; + mClrColorDefault.r = mClrColorDefault.g = mClrColorDefault.b = mClrColorDefault.a = 0.6f; // allocate a single node mScene->mRootNode = new aiNode(); @@ -209,7 +209,7 @@ void STLImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy } pcMat->AddProperty(&clrDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); pcMat->AddProperty(&clrDiffuse, 1, AI_MATKEY_COLOR_SPECULAR); - clrDiffuse = aiColor4D(ai_real(0.05), ai_real(0.05), ai_real(0.05), ai_real(1.0)); + clrDiffuse = aiColor4D(0.05f, 0.05f, 0.05f, 1.0f); pcMat->AddProperty(&clrDiffuse, 1, AI_MATKEY_COLOR_AMBIENT); mScene->mNumMaterials = 1; @@ -257,7 +257,7 @@ void STLImporter::LoadASCIIFile(aiNode *root) { size_t temp = (size_t)(sz - szMe); // setup the name of the node if (temp) { - if (temp >= MAXLEN) { + if (temp >= AI_MAXLEN) { throw DeadlyImportError("STL: Node name too long"); } std::string name(szMe, temp); diff --git a/code/AssetLib/Step/STEPFile.h b/code/AssetLib/Step/STEPFile.h index d8bc0ac49..1fd24b329 100644 --- a/code/AssetLib/Step/STEPFile.h +++ b/code/AssetLib/Step/STEPFile.h @@ -82,8 +82,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // this is intended as stress test - by default, entities are evaluated // lazily and therefore not unless needed. -//#define ASSIMP_IFC_TEST - namespace Assimp { // ******************************************************************************** @@ -531,6 +529,7 @@ public: template const T &To() const { + return dynamic_cast(**this); } @@ -581,12 +580,12 @@ private: }; template -inline bool operator==(const std::shared_ptr &lo, T whatever) { +inline bool operator == (const std::shared_ptr &lo, T whatever) { return *lo == whatever; // XXX use std::forward if we have 0x } template -inline bool operator==(const std::pair> &lo, T whatever) { +inline bool operator == (const std::pair> &lo, T whatever) { return *(lo.second) == whatever; // XXX use std::forward if we have 0x } @@ -599,18 +598,30 @@ struct Lazy { Lazy(const LazyObject *obj = nullptr) : obj(obj) {} operator const T *() const { + if (obj == nullptr) { + throw TypeError("Obj type is nullptr."); + } return obj->ToPtr(); } operator const T &() const { + if (obj == nullptr) { + throw TypeError("Obj type is nullptr."); + } return obj->To(); } const T &operator*() const { + if (obj == nullptr) { + throw TypeError("Obj type is nullptr."); + } return obj->To(); } const T *operator->() const { + if (obj == nullptr) { + throw TypeError("Obj type is nullptr."); + } return &obj->To(); } diff --git a/code/AssetLib/Step/StepExporter.cpp b/code/AssetLib/Step/StepExporter.cpp index 2188ddfec..f5ccf88f6 100644 --- a/code/AssetLib/Step/StepExporter.cpp +++ b/code/AssetLib/Step/StepExporter.cpp @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2024, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -37,11 +36,9 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -@author: Richard Steffen, 2015 ---------------------------------------------------------------------- */ - #ifndef ASSIMP_BUILD_NO_EXPORT #ifndef ASSIMP_BUILD_NO_STEP_EXPORTER diff --git a/code/AssetLib/USD/USDLoader.cpp b/code/AssetLib/USD/USDLoader.cpp new file mode 100644 index 000000000..752332abf --- /dev/null +++ b/code/AssetLib/USD/USDLoader.cpp @@ -0,0 +1,124 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2024, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +---------------------------------------------------------------------- +*/ + +/** @file USDLoader.cpp + * @brief Implementation of the USD importer class + */ + +#ifndef ASSIMP_BUILD_NO_USD_IMPORTER +#include + +// internal headers +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "USDLoader.h" +#include "USDLoaderUtil.h" +#include "USDPreprocessor.h" + +static constexpr aiImporterDesc desc = { + "USD Object Importer", + "", + "", + "https://en.wikipedia.org/wiki/Universal_Scene_Description/", + aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour, + 0, + 0, + 0, + 0, + "usd usda usdc usdz" +}; + +namespace Assimp { +using namespace std; + +// Constructor to be privately used by Importer +USDImporter::USDImporter() : + impl(USDImporterImplTinyusdz()) { +} + +// ------------------------------------------------------------------------------------------------ + +bool USDImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool) const { + // Based on token + static const uint32_t usdcTokens[] = { AI_MAKE_MAGIC("PXR-USDC") }; + bool canRead = CheckMagicToken(pIOHandler, pFile, usdcTokens, AI_COUNT_OF(usdcTokens)); + if (canRead) { + return canRead; + } + + // Based on extension + // TODO: confirm OK to replace this w/SimpleExtensionCheck() below + canRead = isUsd(pFile) || isUsda(pFile) || isUsdc(pFile) || isUsdz(pFile); + if (canRead) { + return canRead; + } + canRead = SimpleExtensionCheck(pFile, "usd", "usda", "usdc", "usdz"); + return canRead; +} + +const aiImporterDesc *USDImporter::GetInfo() const { + return &desc; +} + +void USDImporter::InternReadFile( + const std::string &pFile, + aiScene *pScene, + IOSystem *pIOHandler) { + impl.InternReadFile( + pFile, + pScene, + pIOHandler); +} + +} // namespace Assimp + +#endif // !! ASSIMP_BUILD_NO_USD_IMPORTER + diff --git a/code/AssetLib/USD/USDLoader.h b/code/AssetLib/USD/USDLoader.h new file mode 100644 index 000000000..8400dc42c --- /dev/null +++ b/code/AssetLib/USD/USDLoader.h @@ -0,0 +1,78 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2024, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +---------------------------------------------------------------------- +*/ + +/** @file USDLoader.h + * @brief Declaration of the USD importer class. + */ +#pragma once +#ifndef AI_USDLOADER_H_INCLUDED +#define AI_USDLOADER_H_INCLUDED + +#include +#include +#include +#include + +#include "USDLoaderImplTinyusdz.h" + +namespace Assimp { +class USDImporter : public BaseImporter { +public: + USDImporter(); + ~USDImporter() override = default; + + /// \brief Returns whether the class can handle the format of the given file. + /// \remark See BaseImporter::CanRead() for details. + bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override; + +protected: + //! \brief Appends the supported extension. + const aiImporterDesc *GetInfo() const override; + + void InternReadFile( + const std::string &pFile, + aiScene *pScene, + IOSystem *pIOHandler) override; +private: + USDImporterImplTinyusdz impl; +}; + +} // namespace Assimp +#endif // AI_USDLOADER_H_INCLUDED diff --git a/code/AssetLib/USD/USDLoaderImplTinyusdz.cpp b/code/AssetLib/USD/USDLoaderImplTinyusdz.cpp new file mode 100644 index 000000000..3e32917f9 --- /dev/null +++ b/code/AssetLib/USD/USDLoaderImplTinyusdz.cpp @@ -0,0 +1,749 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2024, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file USDLoader.cpp + * @brief Implementation of the USD importer class + */ + +#ifndef ASSIMP_BUILD_NO_USD_IMPORTER +#include +#include + +// internal headers +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "io-util.hh" // namespace tinyusdz::io +#include "tydra/scene-access.hh" +#include "tydra/shader-network.hh" +#include "USDLoaderImplTinyusdzHelper.h" +#include "USDLoaderImplTinyusdz.h" +#include "USDLoaderUtil.h" +#include "USDPreprocessor.h" + +#include "../../../contrib/tinyusdz/assimp_tinyusdz_logging.inc" + +namespace { + static constexpr char TAG[] = "tinyusdz loader"; +} + +namespace Assimp { +using namespace std; + +void USDImporterImplTinyusdz::InternReadFile( + const std::string &pFile, + aiScene *pScene, + IOSystem *) { + // Grab filename for logging purposes + size_t pos = pFile.find_last_of('/'); + string basePath = pFile.substr(0, pos); + string nameWExt = pFile.substr(pos + 1); + stringstream ss; + ss.str(""); + ss << "InternReadFile(): model" << nameWExt; + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + + bool ret{ false }; + tinyusdz::USDLoadOptions options; + tinyusdz::Stage stage; + std::string warn, err; + bool is_usdz{ false }; + if (isUsdc(pFile)) { + ret = LoadUSDCFromFile(pFile, &stage, &warn, &err, options); + ss.str(""); + ss << "InternReadFile(): LoadUSDCFromFile() result: " << ret; + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + } else if (isUsda(pFile)) { + ret = LoadUSDAFromFile(pFile, &stage, &warn, &err, options); + ss.str(""); + ss << "InternReadFile(): LoadUSDAFromFile() result: " << ret; + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + } else if (isUsdz(pFile)) { + ret = LoadUSDZFromFile(pFile, &stage, &warn, &err, options); + is_usdz = true; + ss.str(""); + ss << "InternReadFile(): LoadUSDZFromFile() result: " << ret; + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + } else if (isUsd(pFile)) { + ret = LoadUSDFromFile(pFile, &stage, &warn, &err, options); + ss.str(""); + ss << "InternReadFile(): LoadUSDFromFile() result: " << ret; + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + } + if (warn.empty() && err.empty()) { + ss.str(""); + ss << "InternReadFile(): load free of warnings/errors"; + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + } else { + if (!warn.empty()) { + ss.str(""); + ss << "InternReadFile(): WARNING reported: " << warn; + TINYUSDZLOGW(TAG, "%s", ss.str().c_str()); + } + if (!err.empty()) { + ss.str(""); + ss << "InternReadFile(): ERROR reported: " << err; + TINYUSDZLOGE(TAG, "%s", ss.str().c_str()); + } + } + if (!ret) { + ss.str(""); + ss << "InternReadFile(): ERROR: load failed! ret: " << ret; + TINYUSDZLOGE(TAG, "%s", ss.str().c_str()); + return; + } + tinyusdz::tydra::RenderScene render_scene; + tinyusdz::tydra::RenderSceneConverter converter; + tinyusdz::tydra::RenderSceneConverterEnv env(stage); + std::string usd_basedir = tinyusdz::io::GetBaseDir(pFile); + env.set_search_paths({ usd_basedir }); // {} needed to convert to vector of char + + // NOTE: Pointer address of usdz_asset must be valid until the call of RenderSceneConverter::ConvertToRenderScene. + tinyusdz::USDZAsset usdz_asset; + if (is_usdz) { + if (!tinyusdz::ReadUSDZAssetInfoFromFile(pFile, &usdz_asset, &warn, &err)) { + if (!warn.empty()) { + ss.str(""); + ss << "InternReadFile(): ReadUSDZAssetInfoFromFile: WARNING reported: " << warn; + TINYUSDZLOGW(TAG, "%s", ss.str().c_str()); + } + if (!err.empty()) { + ss.str(""); + ss << "InternReadFile(): ReadUSDZAssetInfoFromFile: ERROR reported: " << err; + TINYUSDZLOGE(TAG, "%s", ss.str().c_str()); + } + ss.str(""); + ss << "InternReadFile(): ReadUSDZAssetInfoFromFile: ERROR!"; + TINYUSDZLOGE(TAG, "%s", ss.str().c_str()); + } else { + ss.str(""); + ss << "InternReadFile(): ReadUSDZAssetInfoFromFile: OK"; + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + } + + tinyusdz::AssetResolutionResolver arr; + if (!tinyusdz::SetupUSDZAssetResolution(arr, &usdz_asset)) { + ss.str(""); + ss << "InternReadFile(): SetupUSDZAssetResolution: ERROR: load failed! ret: " << ret; + TINYUSDZLOGE(TAG, "%s", ss.str().c_str()); + } else { + ss.str(""); + ss << "InternReadFile(): SetupUSDZAssetResolution: OK"; + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + env.asset_resolver = arr; + } + } + + ret = converter.ConvertToRenderScene(env, &render_scene); + if (!ret) { + ss.str(""); + ss << "InternReadFile(): ConvertToRenderScene() failed!"; + TINYUSDZLOGE(TAG, "%s", ss.str().c_str()); + return; + } + +// sanityCheckNodesRecursive(pScene->mRootNode); + meshes(render_scene, pScene, nameWExt); + materials(render_scene, pScene, nameWExt); + textures(render_scene, pScene, nameWExt); + textureImages(render_scene, pScene, nameWExt); + buffers(render_scene, pScene, nameWExt); + + std::map meshNodes; + setupNodes(render_scene, pScene, meshNodes, nameWExt); + + setupBlendShapes(render_scene, pScene, nameWExt); +} + +void USDImporterImplTinyusdz::meshes( + const tinyusdz::tydra::RenderScene &render_scene, + aiScene *pScene, + const std::string &nameWExt) { + stringstream ss; + pScene->mNumMeshes = static_cast(render_scene.meshes.size()); + pScene->mMeshes = new aiMesh *[pScene->mNumMeshes](); + ss.str(""); + ss << "meshes(): pScene->mNumMeshes: " << pScene->mNumMeshes; + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + + // Export meshes + for (size_t meshIdx = 0; meshIdx < pScene->mNumMeshes; meshIdx++) { + pScene->mMeshes[meshIdx] = new aiMesh(); + pScene->mMeshes[meshIdx]->mName.Set(render_scene.meshes[meshIdx].prim_name); + ss.str(""); + ss << " mesh[" << meshIdx << "]: " << + render_scene.meshes[meshIdx].joint_and_weights.jointIndices.size() << " jointIndices, " << + render_scene.meshes[meshIdx].joint_and_weights.jointWeights.size() << " jointWeights, elementSize: " << + render_scene.meshes[meshIdx].joint_and_weights.elementSize; + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + ss.str(""); + ss << " skel_id: " << render_scene.meshes[meshIdx].skel_id; + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + if (render_scene.meshes[meshIdx].material_id > -1) { + pScene->mMeshes[meshIdx]->mMaterialIndex = render_scene.meshes[meshIdx].material_id; + } + verticesForMesh(render_scene, pScene, meshIdx, nameWExt); + facesForMesh(render_scene, pScene, meshIdx, nameWExt); + // Some models infer normals from faces, but others need them e.g. + // - apple "toy car" canopy normals will be wrong + // - human "untitled" model (tinyusdz issue #115) will be "splotchy" + normalsForMesh(render_scene, pScene, meshIdx, nameWExt); + materialsForMesh(render_scene, pScene, meshIdx, nameWExt); + uvsForMesh(render_scene, pScene, meshIdx, nameWExt); + } +} + +void USDImporterImplTinyusdz::verticesForMesh( + const tinyusdz::tydra::RenderScene &render_scene, + aiScene *pScene, + size_t meshIdx, + const std::string &nameWExt) { + UNUSED(nameWExt); + pScene->mMeshes[meshIdx]->mNumVertices = static_cast(render_scene.meshes[meshIdx].points.size()); + pScene->mMeshes[meshIdx]->mVertices = new aiVector3D[pScene->mMeshes[meshIdx]->mNumVertices]; + for (size_t j = 0; j < pScene->mMeshes[meshIdx]->mNumVertices; ++j) { + pScene->mMeshes[meshIdx]->mVertices[j].x = render_scene.meshes[meshIdx].points[j][0]; + pScene->mMeshes[meshIdx]->mVertices[j].y = render_scene.meshes[meshIdx].points[j][1]; + pScene->mMeshes[meshIdx]->mVertices[j].z = render_scene.meshes[meshIdx].points[j][2]; + } +} + +void USDImporterImplTinyusdz::facesForMesh( + const tinyusdz::tydra::RenderScene &render_scene, + aiScene *pScene, + size_t meshIdx, + const std::string &nameWExt) { + UNUSED(nameWExt); + pScene->mMeshes[meshIdx]->mNumFaces = static_cast(render_scene.meshes[meshIdx].faceVertexCounts().size()); + pScene->mMeshes[meshIdx]->mFaces = new aiFace[pScene->mMeshes[meshIdx]->mNumFaces](); + size_t faceVertIdxOffset = 0; + for (size_t faceIdx = 0; faceIdx < pScene->mMeshes[meshIdx]->mNumFaces; ++faceIdx) { + pScene->mMeshes[meshIdx]->mFaces[faceIdx].mNumIndices = render_scene.meshes[meshIdx].faceVertexCounts()[faceIdx]; + pScene->mMeshes[meshIdx]->mFaces[faceIdx].mIndices = new unsigned int[pScene->mMeshes[meshIdx]->mFaces[faceIdx].mNumIndices]; + for (size_t j = 0; j < pScene->mMeshes[meshIdx]->mFaces[faceIdx].mNumIndices; ++j) { + pScene->mMeshes[meshIdx]->mFaces[faceIdx].mIndices[j] = + render_scene.meshes[meshIdx].faceVertexIndices()[j + faceVertIdxOffset]; + } + faceVertIdxOffset += pScene->mMeshes[meshIdx]->mFaces[faceIdx].mNumIndices; + } +} + +void USDImporterImplTinyusdz::normalsForMesh( + const tinyusdz::tydra::RenderScene &render_scene, + aiScene *pScene, + size_t meshIdx, + const std::string &nameWExt) { + UNUSED(nameWExt); + pScene->mMeshes[meshIdx]->mNormals = new aiVector3D[pScene->mMeshes[meshIdx]->mNumVertices]; + const float *floatPtr = reinterpret_cast(render_scene.meshes[meshIdx].normals.get_data().data()); + for (size_t vertIdx = 0, fpj = 0; vertIdx < pScene->mMeshes[meshIdx]->mNumVertices; ++vertIdx, fpj += 3) { + pScene->mMeshes[meshIdx]->mNormals[vertIdx].x = floatPtr[fpj]; + pScene->mMeshes[meshIdx]->mNormals[vertIdx].y = floatPtr[fpj + 1]; + pScene->mMeshes[meshIdx]->mNormals[vertIdx].z = floatPtr[fpj + 2]; + } +} + +void USDImporterImplTinyusdz::materialsForMesh( + const tinyusdz::tydra::RenderScene &render_scene, + aiScene *pScene, + size_t meshIdx, + const std::string &nameWExt) { + UNUSED(render_scene); UNUSED(pScene); UNUSED(meshIdx); UNUSED(nameWExt); +} + +void USDImporterImplTinyusdz::uvsForMesh( + const tinyusdz::tydra::RenderScene &render_scene, + aiScene *pScene, + size_t meshIdx, + const std::string &nameWExt) { + UNUSED(nameWExt); + const size_t uvSlotsCount = render_scene.meshes[meshIdx].texcoords.size(); + if (uvSlotsCount < 1) { + return; + } + pScene->mMeshes[meshIdx]->mTextureCoords[0] = new aiVector3D[pScene->mMeshes[meshIdx]->mNumVertices]; + pScene->mMeshes[meshIdx]->mNumUVComponents[0] = 2; // U and V stored in "x", "y" of aiVector3D. + for (unsigned int uvSlotIdx = 0; uvSlotIdx < uvSlotsCount; ++uvSlotIdx) { + const auto uvsForSlot = render_scene.meshes[meshIdx].texcoords.at(uvSlotIdx); + if (uvsForSlot.get_data().size() == 0) { + continue; + } + const float *floatPtr = reinterpret_cast(uvsForSlot.get_data().data()); + for (size_t vertIdx = 0, fpj = 0; vertIdx < pScene->mMeshes[meshIdx]->mNumVertices; ++vertIdx, fpj += 2) { + pScene->mMeshes[meshIdx]->mTextureCoords[uvSlotIdx][vertIdx].x = floatPtr[fpj]; + pScene->mMeshes[meshIdx]->mTextureCoords[uvSlotIdx][vertIdx].y = floatPtr[fpj + 1]; + } + } +} + +static aiColor3D *ownedColorPtrFor(const std::array &color) { + aiColor3D *colorPtr = new aiColor3D(); + colorPtr->r = color[0]; + colorPtr->g = color[1]; + colorPtr->b = color[2]; + return colorPtr; +} + +static std::string nameForTextureWithId( + const tinyusdz::tydra::RenderScene &render_scene, + const int targetId) { + stringstream ss; + std::string texName; + for (const auto &image : render_scene.images) { + if (image.buffer_id == targetId) { + texName = image.asset_identifier; + ss.str(""); + ss << "nameForTextureWithId(): found texture " << texName << " with target id " << targetId; + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + break; + } + } + ss.str(""); + ss << "nameForTextureWithId(): ERROR! Failed to find texture with target id " << targetId; + TINYUSDZLOGE(TAG, "%s", ss.str().c_str()); + return texName; +} + +static void assignTexture( + const tinyusdz::tydra::RenderScene &render_scene, + const tinyusdz::tydra::RenderMaterial &material, + aiMaterial *mat, + const int textureId, + const int aiTextureType) { + UNUSED(material); + std::string name = nameForTextureWithId(render_scene, textureId); + aiString *texName = new aiString(); + texName->Set(name); + stringstream ss; + ss.str(""); + ss << "assignTexture(): name: " << name; + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + // TODO: verify hard-coded '0' index is correct + mat->AddProperty(texName, _AI_MATKEY_TEXTURE_BASE, aiTextureType, 0); +} + +void USDImporterImplTinyusdz::materials( + const tinyusdz::tydra::RenderScene &render_scene, + aiScene *pScene, + const std::string &nameWExt) { + const size_t numMaterials{render_scene.materials.size()}; + (void) numMaterials; // Ignore unused variable when -Werror enabled + stringstream ss; + ss.str(""); + ss << "materials(): model" << nameWExt << ", numMaterials: " << numMaterials; + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + pScene->mNumMaterials = 0; + if (render_scene.materials.empty()) { + return; + } + pScene->mMaterials = new aiMaterial *[render_scene.materials.size()]; + for (const auto &material : render_scene.materials) { + ss.str(""); + ss << " material[" << pScene->mNumMaterials << "]: name: |" << material.name << "|, disp name: |" << material.display_name << "|"; + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + aiMaterial *mat = new aiMaterial; + + aiString *materialName = new aiString(); + materialName->Set(material.name); + mat->AddProperty(materialName, AI_MATKEY_NAME); + + mat->AddProperty( + ownedColorPtrFor(material.surfaceShader.diffuseColor.value), + 1, AI_MATKEY_COLOR_DIFFUSE); + mat->AddProperty( + ownedColorPtrFor(material.surfaceShader.specularColor.value), + 1, AI_MATKEY_COLOR_SPECULAR); + mat->AddProperty( + ownedColorPtrFor(material.surfaceShader.emissiveColor.value), + 1, AI_MATKEY_COLOR_EMISSIVE); + + ss.str(""); + if (material.surfaceShader.diffuseColor.is_texture()) { + assignTexture(render_scene, material, mat, material.surfaceShader.diffuseColor.texture_id, aiTextureType_DIFFUSE); + ss << " material[" << pScene->mNumMaterials << "]: diff tex id " << material.surfaceShader.diffuseColor.texture_id << "\n"; + } + if (material.surfaceShader.specularColor.is_texture()) { + assignTexture(render_scene, material, mat, material.surfaceShader.specularColor.texture_id, aiTextureType_SPECULAR); + ss << " material[" << pScene->mNumMaterials << "]: spec tex id " << material.surfaceShader.specularColor.texture_id << "\n"; + } + if (material.surfaceShader.normal.is_texture()) { + assignTexture(render_scene, material, mat, material.surfaceShader.normal.texture_id, aiTextureType_NORMALS); + ss << " material[" << pScene->mNumMaterials << "]: normal tex id " << material.surfaceShader.normal.texture_id << "\n"; + } + if (material.surfaceShader.emissiveColor.is_texture()) { + assignTexture(render_scene, material, mat, material.surfaceShader.emissiveColor.texture_id, aiTextureType_EMISSIVE); + ss << " material[" << pScene->mNumMaterials << "]: emissive tex id " << material.surfaceShader.emissiveColor.texture_id << "\n"; + } + if (material.surfaceShader.occlusion.is_texture()) { + assignTexture(render_scene, material, mat, material.surfaceShader.occlusion.texture_id, aiTextureType_LIGHTMAP); + ss << " material[" << pScene->mNumMaterials << "]: lightmap (occlusion) tex id " << material.surfaceShader.occlusion.texture_id << "\n"; + } + if (material.surfaceShader.metallic.is_texture()) { + assignTexture(render_scene, material, mat, material.surfaceShader.metallic.texture_id, aiTextureType_METALNESS); + ss << " material[" << pScene->mNumMaterials << "]: metallic tex id " << material.surfaceShader.metallic.texture_id << "\n"; + } + if (material.surfaceShader.roughness.is_texture()) { + assignTexture(render_scene, material, mat, material.surfaceShader.roughness.texture_id, aiTextureType_DIFFUSE_ROUGHNESS); + ss << " material[" << pScene->mNumMaterials << "]: roughness tex id " << material.surfaceShader.roughness.texture_id << "\n"; + } + if (material.surfaceShader.clearcoat.is_texture()) { + assignTexture(render_scene, material, mat, material.surfaceShader.clearcoat.texture_id, aiTextureType_CLEARCOAT); + ss << " material[" << pScene->mNumMaterials << "]: clearcoat tex id " << material.surfaceShader.clearcoat.texture_id << "\n"; + } + if (material.surfaceShader.opacity.is_texture()) { + assignTexture(render_scene, material, mat, material.surfaceShader.opacity.texture_id, aiTextureType_OPACITY); + ss << " material[" << pScene->mNumMaterials << "]: opacity tex id " << material.surfaceShader.opacity.texture_id << "\n"; + } + if (material.surfaceShader.displacement.is_texture()) { + assignTexture(render_scene, material, mat, material.surfaceShader.displacement.texture_id, aiTextureType_DISPLACEMENT); + ss << " material[" << pScene->mNumMaterials << "]: displacement tex id " << material.surfaceShader.displacement.texture_id << "\n"; + } + if (material.surfaceShader.clearcoatRoughness.is_texture()) { + ss << " material[" << pScene->mNumMaterials << "]: clearcoatRoughness tex id " << material.surfaceShader.clearcoatRoughness.texture_id << "\n"; + } + if (material.surfaceShader.opacityThreshold.is_texture()) { + ss << " material[" << pScene->mNumMaterials << "]: opacityThreshold tex id " << material.surfaceShader.opacityThreshold.texture_id << "\n"; + } + if (material.surfaceShader.ior.is_texture()) { + ss << " material[" << pScene->mNumMaterials << "]: ior tex id " << material.surfaceShader.ior.texture_id << "\n"; + } + if (!ss.str().empty()) { + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + } + + pScene->mMaterials[pScene->mNumMaterials] = mat; + ++pScene->mNumMaterials; + } +} + +void USDImporterImplTinyusdz::textures( + const tinyusdz::tydra::RenderScene &render_scene, + aiScene *pScene, + const std::string &nameWExt) { + UNUSED(pScene); + const size_t numTextures{render_scene.textures.size()}; + UNUSED(numTextures); // Ignore unused variable when -Werror enabled + stringstream ss; + ss.str(""); + ss << "textures(): model" << nameWExt << ", numTextures: " << numTextures; + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + size_t i{0}; + UNUSED(i); + for (const auto &texture : render_scene.textures) { + UNUSED(texture); + ss.str(""); + ss << " texture[" << i << "]: id: " << texture.texture_image_id << ", disp name: |" << texture.display_name << "|, varname_uv: " << + texture.varname_uv << ", prim_name: |" << texture.prim_name << "|, abs_path: |" << texture.abs_path << "|"; + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + ++i; + } +} + +/** + * "owned" as in, used "new" to allocate and aiScene now responsible for "delete" + * + * @param render_scene renderScene object + * @param image textureImage object + * @param nameWExt filename w/ext (use to extract file type hint) + * @return aiTexture ptr + */ +static aiTexture *ownedEmbeddedTextureFor( + const tinyusdz::tydra::RenderScene &render_scene, + const tinyusdz::tydra::TextureImage &image, + const std::string &nameWExt) { + UNUSED(nameWExt); + stringstream ss; + aiTexture *tex = new aiTexture(); + size_t pos = image.asset_identifier.find_last_of('/'); + string embTexName{image.asset_identifier.substr(pos + 1)}; + tex->mFilename.Set(image.asset_identifier.c_str()); + tex->mHeight = image.height; +// const size_t imageBytesCount{render_scene.buffers[image.buffer_id].data.size() / image.channels}; + tex->mWidth = image.width; + if (tex->mHeight == 0) { + pos = embTexName.find_last_of('.'); + strncpy(tex->achFormatHint, embTexName.substr(pos + 1).c_str(), 3); + const size_t imageBytesCount{render_scene.buffers[image.buffer_id].data.size()}; + tex->pcData = (aiTexel *) new char[imageBytesCount]; + memcpy(tex->pcData, &render_scene.buffers[image.buffer_id].data[0], imageBytesCount); + } else { + string formatHint{"rgba8888"}; + strncpy(tex->achFormatHint, formatHint.c_str(), 8); + const size_t imageTexelsCount{tex->mWidth * tex->mHeight}; + tex->pcData = (aiTexel *) new char[imageTexelsCount * image.channels]; + const float *floatPtr = reinterpret_cast(&render_scene.buffers[image.buffer_id].data[0]); + ss.str(""); + ss << "ownedEmbeddedTextureFor(): manual fill..."; + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + for (size_t i = 0, fpi = 0; i < imageTexelsCount; ++i, fpi += 4) { + tex->pcData[i].b = static_cast(floatPtr[fpi] * 255); + tex->pcData[i].g = static_cast(floatPtr[fpi + 1] * 255); + tex->pcData[i].r = static_cast(floatPtr[fpi + 2] * 255); + tex->pcData[i].a = static_cast(floatPtr[fpi + 3] * 255); + } + ss.str(""); + ss << "ownedEmbeddedTextureFor(): imageTexelsCount: " << imageTexelsCount << ", channels: " << image.channels; + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + } + return tex; +} + +void USDImporterImplTinyusdz::textureImages( + const tinyusdz::tydra::RenderScene &render_scene, + aiScene *pScene, + const std::string &nameWExt) { + stringstream ss; + const size_t numTextureImages{render_scene.images.size()}; + UNUSED(numTextureImages); // Ignore unused variable when -Werror enabled + ss.str(""); + ss << "textureImages(): model" << nameWExt << ", numTextureImages: " << numTextureImages; + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + pScene->mTextures = nullptr; // Need to iterate over images before knowing if valid textures available + pScene->mNumTextures = 0; + for (const auto &image : render_scene.images) { + ss.str(""); + ss << " image[" << pScene->mNumTextures << "]: |" << image.asset_identifier << "| w: " << image.width << ", h: " << image.height << + ", channels: " << image.channels << ", miplevel: " << image.miplevel << ", buffer id: " << image.buffer_id << "\n" << + " buffers.size(): " << render_scene.buffers.size() << ", data empty? " << render_scene.buffers[image.buffer_id].data.empty(); + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + if (image.buffer_id > -1 && + image.buffer_id < static_cast(render_scene.buffers.size()) && + !render_scene.buffers[image.buffer_id].data.empty()) { + aiTexture *tex = ownedEmbeddedTextureFor( + render_scene, + image, + nameWExt); + if (pScene->mTextures == nullptr) { + ss.str(""); + ss << " Init pScene->mTextures[" << render_scene.images.size() << "]"; + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + pScene->mTextures = new aiTexture *[render_scene.images.size()]; + } + ss.str(""); + ss << " pScene->mTextures[" << pScene->mNumTextures << "] name: |" << tex->mFilename.C_Str() << + "|, w: " << tex->mWidth << ", h: " << tex->mHeight << ", hint: " << tex->achFormatHint; + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + pScene->mTextures[pScene->mNumTextures++] = tex; + } + } +} + +void USDImporterImplTinyusdz::buffers( + const tinyusdz::tydra::RenderScene &render_scene, + aiScene *pScene, + const std::string &nameWExt) { + const size_t numBuffers{render_scene.buffers.size()}; + UNUSED(pScene); UNUSED(numBuffers); // Ignore unused variable when -Werror enabled + stringstream ss; + ss.str(""); + ss << "buffers(): model" << nameWExt << ", numBuffers: " << numBuffers; + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + size_t i = 0; + for (const auto &buffer : render_scene.buffers) { + ss.str(""); + ss << " buffer[" << i << "]: count: " << buffer.data.size() << ", type: " << to_string(buffer.componentType); + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + ++i; + } +} + +void USDImporterImplTinyusdz::setupNodes( + const tinyusdz::tydra::RenderScene &render_scene, + aiScene *pScene, + std::map &meshNodes, + const std::string &nameWExt) { + stringstream ss; + + pScene->mRootNode = nodes(render_scene, meshNodes, nameWExt); + pScene->mRootNode->mNumMeshes = pScene->mNumMeshes; + pScene->mRootNode->mMeshes = new unsigned int[pScene->mRootNode->mNumMeshes]; + ss.str(""); + ss << "setupNodes(): pScene->mNumMeshes: " << pScene->mNumMeshes; + if (pScene->mRootNode != nullptr) { + ss << ", mRootNode->mNumMeshes: " << pScene->mRootNode->mNumMeshes; + } + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + + for (unsigned int meshIdx = 0; meshIdx < pScene->mNumMeshes; meshIdx++) { + pScene->mRootNode->mMeshes[meshIdx] = meshIdx; + } + +} + +aiNode *USDImporterImplTinyusdz::nodes( + const tinyusdz::tydra::RenderScene &render_scene, + std::map &meshNodes, + const std::string &nameWExt) { + const size_t numNodes{render_scene.nodes.size()}; + (void) numNodes; // Ignore unused variable when -Werror enabled + stringstream ss; + ss.str(""); + ss << "nodes(): model" << nameWExt << ", numNodes: " << numNodes; + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + return nodesRecursive(nullptr, render_scene.nodes[0], meshNodes); +} + +using Assimp::tinyusdzNodeTypeFor; +using Assimp::tinyUsdzMat4ToAiMat4; +using tinyusdz::tydra::NodeType; +aiNode *USDImporterImplTinyusdz::nodesRecursive( + aiNode *pNodeParent, + const tinyusdz::tydra::Node &node, + std::map &meshNodes) { + stringstream ss; + aiNode *cNode = new aiNode(); + cNode->mParent = pNodeParent; + cNode->mName.Set(node.prim_name); + cNode->mTransformation = tinyUsdzMat4ToAiMat4(node.local_matrix.m); + ss.str(""); + ss << "nodesRecursive(): node " << cNode->mName.C_Str() << + " type: |" << tinyusdzNodeTypeFor(node.nodeType) << + "|, disp " << node.display_name << ", abs " << node.abs_path; + if (cNode->mParent != nullptr) { + ss << " (parent " << cNode->mParent->mName.C_Str() << ")"; + } + ss << " has " << node.children.size() << " children"; + if (node.id > -1) { + ss << "\n node mesh id: " << node.id << " (node type: " << tinyusdzNodeTypeFor(node.nodeType) << ")"; + meshNodes[node.id] = node; + } + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + if (!node.children.empty()) { + cNode->mNumChildren = static_cast(node.children.size()); + cNode->mChildren = new aiNode *[cNode->mNumChildren]; + } + + size_t i{0}; + for (const auto &childNode: node.children) { + cNode->mChildren[i] = nodesRecursive(cNode, childNode, meshNodes); + ++i; + } + return cNode; +} + +void USDImporterImplTinyusdz::sanityCheckNodesRecursive( + aiNode *cNode) { + stringstream ss; + ss.str(""); + ss << "sanityCheckNodesRecursive(): node " << cNode->mName.C_Str(); + if (cNode->mParent != nullptr) { + ss << " (parent " << cNode->mParent->mName.C_Str() << ")"; + } + ss << " has " << cNode->mNumChildren << " children"; + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + for (size_t i = 0; i < cNode->mNumChildren; ++i) { + sanityCheckNodesRecursive(cNode->mChildren[i]); + } +} + +void USDImporterImplTinyusdz::setupBlendShapes( + const tinyusdz::tydra::RenderScene &render_scene, + aiScene *pScene, + const std::string &nameWExt) { + stringstream ss; + ss.str(""); + ss << "setupBlendShapes(): iterating over " << pScene->mNumMeshes << " meshes for model" << nameWExt; + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + for (size_t meshIdx = 0; meshIdx < pScene->mNumMeshes; meshIdx++) { + blendShapesForMesh(render_scene, pScene, meshIdx, nameWExt); + } +} + +void USDImporterImplTinyusdz::blendShapesForMesh( + const tinyusdz::tydra::RenderScene &render_scene, + aiScene *pScene, + size_t meshIdx, + const std::string &nameWExt) { + UNUSED(nameWExt); + stringstream ss; + const unsigned int numBlendShapeTargets{static_cast(render_scene.meshes[meshIdx].targets.size())}; + UNUSED(numBlendShapeTargets); // Ignore unused variable when -Werror enabled + ss.str(""); + ss << " blendShapesForMesh(): mesh[" << meshIdx << "], numBlendShapeTargets: " << numBlendShapeTargets; + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + if (numBlendShapeTargets > 0) { + pScene->mMeshes[meshIdx]->mNumAnimMeshes = numBlendShapeTargets; + pScene->mMeshes[meshIdx]->mAnimMeshes = new aiAnimMesh *[pScene->mMeshes[meshIdx]->mNumAnimMeshes]; + } + auto mapIter = render_scene.meshes[meshIdx].targets.begin(); + size_t animMeshIdx{0}; + for (; mapIter != render_scene.meshes[meshIdx].targets.end(); ++mapIter) { + const std::string name{mapIter->first}; + const tinyusdz::tydra::ShapeTarget shapeTarget{mapIter->second}; + pScene->mMeshes[meshIdx]->mAnimMeshes[animMeshIdx] = aiCreateAnimMesh(pScene->mMeshes[meshIdx]); + ss.str(""); + ss << " mAnimMeshes[" << animMeshIdx << "]: mNumVertices: " << pScene->mMeshes[meshIdx]->mAnimMeshes[animMeshIdx]->mNumVertices << + ", target: " << shapeTarget.pointIndices.size() << " pointIndices, " << shapeTarget.pointOffsets.size() << + " pointOffsets, " << shapeTarget.normalOffsets.size() << " normalOffsets"; + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + for (size_t iVert = 0; iVert < shapeTarget.pointOffsets.size(); ++iVert) { + pScene->mMeshes[meshIdx]->mAnimMeshes[animMeshIdx]->mVertices[shapeTarget.pointIndices[iVert]] += + tinyUsdzScaleOrPosToAssimp(shapeTarget.pointOffsets[iVert]); + } + for (size_t iVert = 0; iVert < shapeTarget.normalOffsets.size(); ++iVert) { + pScene->mMeshes[meshIdx]->mAnimMeshes[animMeshIdx]->mNormals[shapeTarget.pointIndices[iVert]] += + tinyUsdzScaleOrPosToAssimp(shapeTarget.normalOffsets[iVert]); + } + ss.str(""); + ss << " target[" << animMeshIdx << "]: name: " << name << ", prim_name: " << + shapeTarget.prim_name << ", abs_path: " << shapeTarget.abs_path << + ", display_name: " << shapeTarget.display_name << ", " << shapeTarget.pointIndices.size() << + " pointIndices, " << shapeTarget.pointOffsets.size() << " pointOffsets, " << + shapeTarget.normalOffsets.size() << " normalOffsets, " << shapeTarget.inbetweens.size() << + " inbetweens"; + TINYUSDZLOGD(TAG, "%s", ss.str().c_str()); + ++animMeshIdx; + } +} + +} // namespace Assimp + +#endif // !! ASSIMP_BUILD_NO_USD_IMPORTER diff --git a/code/AssetLib/USD/USDLoaderImplTinyusdz.h b/code/AssetLib/USD/USDLoaderImplTinyusdz.h new file mode 100644 index 000000000..69f8c125c --- /dev/null +++ b/code/AssetLib/USD/USDLoaderImplTinyusdz.h @@ -0,0 +1,155 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2024, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file USDLoader.h + * @brief Declaration of the USD importer class. + */ +#pragma once +#ifndef AI_USDLOADER_IMPL_TINYUSDZ_H_INCLUDED +#define AI_USDLOADER_IMPL_TINYUSDZ_H_INCLUDED + +#include +#include +#include +#include +#include +#include "tinyusdz.hh" +#include "tydra/render-data.hh" + +namespace Assimp { +class USDImporterImplTinyusdz { +public: + USDImporterImplTinyusdz() = default; + ~USDImporterImplTinyusdz() = default; + + void InternReadFile( + const std::string &pFile, + aiScene *pScene, + IOSystem *pIOHandler); + + void meshes( + const tinyusdz::tydra::RenderScene &render_scene, + aiScene *pScene, + const std::string &nameWExt); + + void verticesForMesh( + const tinyusdz::tydra::RenderScene &render_scene, + aiScene *pScene, + size_t meshIdx, + const std::string &nameWExt); + + void facesForMesh( + const tinyusdz::tydra::RenderScene &render_scene, + aiScene *pScene, + size_t meshIdx, + const std::string &nameWExt); + + void normalsForMesh( + const tinyusdz::tydra::RenderScene &render_scene, + aiScene *pScene, + size_t meshIdx, + const std::string &nameWExt); + + void materialsForMesh( + const tinyusdz::tydra::RenderScene &render_scene, + aiScene *pScene, + size_t meshIdx, + const std::string &nameWExt); + + void uvsForMesh( + const tinyusdz::tydra::RenderScene &render_scene, + aiScene *pScene, + size_t meshIdx, + const std::string &nameWExt); + + void materials( + const tinyusdz::tydra::RenderScene &render_scene, + aiScene *pScene, + const std::string &nameWExt); + + void textures( + const tinyusdz::tydra::RenderScene &render_scene, + aiScene *pScene, + const std::string &nameWExt); + + void textureImages( + const tinyusdz::tydra::RenderScene &render_scene, + aiScene *pScene, + const std::string &nameWExt); + + void buffers( + const tinyusdz::tydra::RenderScene &render_scene, + aiScene *pScene, + const std::string &nameWExt); + + void setupNodes( + const tinyusdz::tydra::RenderScene &render_scene, + aiScene *pScene, + std::map &meshNodes, + const std::string &nameWExt + ); + + aiNode *nodes( + const tinyusdz::tydra::RenderScene &render_scene, + std::map &meshNodes, + const std::string &nameWExt); + + aiNode *nodesRecursive( + aiNode *pNodeParent, + const tinyusdz::tydra::Node &node, + std::map &meshNodes); + + void sanityCheckNodesRecursive( + aiNode *pNode); + + void setupBlendShapes( + const tinyusdz::tydra::RenderScene &render_scene, + aiScene *pScene, + const std::string &nameWExt); + + void blendShapesForMesh( + const tinyusdz::tydra::RenderScene &render_scene, + aiScene *pScene, + size_t meshIdx, + const std::string &nameWExt); +}; +} // namespace Assimp +#endif // AI_USDLOADER_IMPL_TINYUSDZ_H_INCLUDED diff --git a/code/AssetLib/USD/USDLoaderImplTinyusdzHelper.cpp b/code/AssetLib/USD/USDLoaderImplTinyusdzHelper.cpp new file mode 100644 index 000000000..09d692445 --- /dev/null +++ b/code/AssetLib/USD/USDLoaderImplTinyusdzHelper.cpp @@ -0,0 +1,151 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2024, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#ifndef ASSIMP_BUILD_NO_USD_IMPORTER +#include "USDLoaderImplTinyusdzHelper.h" + +#include "../../../contrib/tinyusdz/assimp_tinyusdz_logging.inc" + +namespace { +//const char *const TAG = "tinyusdz helper"; +} + +using ChannelType = tinyusdz::tydra::AnimationChannel::ChannelType; +std::string Assimp::tinyusdzAnimChannelTypeFor(ChannelType animChannel) { + switch (animChannel) { + case ChannelType::Transform: { + return "Transform"; + } + case ChannelType::Translation: { + return "Translation"; + } + case ChannelType::Rotation: { + return "Rotation"; + } + case ChannelType::Scale: { + return "Scale"; + } + case ChannelType::Weight: { + return "Weight"; + } + default: + return "Invalid"; + } +} + +using tinyusdz::tydra::NodeType; +std::string Assimp::tinyusdzNodeTypeFor(NodeType type) { + switch (type) { + case NodeType::Xform: { + return "Xform"; + } + case NodeType::Mesh: { + return "Mesh"; + } + case NodeType::Camera: { + return "Camera"; + } + case NodeType::Skeleton: { + return "Skeleton"; + } + case NodeType::PointLight: { + return "PointLight"; + } + case NodeType::DirectionalLight: { + return "DirectionalLight"; + } + case NodeType::EnvmapLight: { + return "EnvmapLight"; + } + default: + return "Invalid"; + } +} + +aiMatrix4x4 Assimp::tinyUsdzMat4ToAiMat4(const double matIn[4][4]) { + aiMatrix4x4 matOut; + matOut.a1 = matIn[0][0]; + matOut.a2 = matIn[0][1]; + matOut.a3 = matIn[0][2]; + matOut.a4 = matIn[0][3]; + matOut.b1 = matIn[1][0]; + matOut.b2 = matIn[1][1]; + matOut.b3 = matIn[1][2]; + matOut.b4 = matIn[1][3]; + matOut.c1 = matIn[2][0]; + matOut.c2 = matIn[2][1]; + matOut.c3 = matIn[2][2]; + matOut.c4 = matIn[2][3]; + matOut.d1 = matIn[3][0]; + matOut.d2 = matIn[3][1]; + matOut.d3 = matIn[3][2]; + matOut.d4 = matIn[3][3]; +// matOut.a1 = matIn[0][0]; +// matOut.a2 = matIn[1][0]; +// matOut.a3 = matIn[2][0]; +// matOut.a4 = matIn[3][0]; +// matOut.b1 = matIn[0][1]; +// matOut.b2 = matIn[1][1]; +// matOut.b3 = matIn[2][1]; +// matOut.b4 = matIn[3][1]; +// matOut.c1 = matIn[0][2]; +// matOut.c2 = matIn[1][2]; +// matOut.c3 = matIn[2][2]; +// matOut.c4 = matIn[3][2]; +// matOut.d1 = matIn[0][3]; +// matOut.d2 = matIn[1][3]; +// matOut.d3 = matIn[2][3]; +// matOut.d4 = matIn[3][3]; + return matOut; +} + +aiVector3D Assimp::tinyUsdzScaleOrPosToAssimp(const std::array &scaleOrPosIn) { + return aiVector3D(scaleOrPosIn[0], scaleOrPosIn[1], scaleOrPosIn[2]); +} + +aiQuaternion Assimp::tinyUsdzQuatToAiQuat(const std::array &quatIn) { + // tinyusdz "quat" is x,y,z,w + // aiQuaternion is w,x,y,z + return aiQuaternion( + quatIn[3], quatIn[0], quatIn[1], quatIn[2]); +} + +#endif // !! ASSIMP_BUILD_NO_USD_IMPORTER diff --git a/code/AssetLib/USD/USDLoaderImplTinyusdzHelper.h b/code/AssetLib/USD/USDLoaderImplTinyusdzHelper.h new file mode 100644 index 000000000..c5eaafd73 --- /dev/null +++ b/code/AssetLib/USD/USDLoaderImplTinyusdzHelper.h @@ -0,0 +1,70 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2024, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#pragma once +#ifndef AI_USDLOADER_IMPL_TINYUSDZ_HELPER_H_INCLUDED +#define AI_USDLOADER_IMPL_TINYUSDZ_HELPER_H_INCLUDED + +#include +#include +#include +#include "tinyusdz.hh" +#include "tydra/render-data.hh" + +namespace Assimp { + +std::string tinyusdzAnimChannelTypeFor( + tinyusdz::tydra::AnimationChannel::ChannelType animChannel); +std::string tinyusdzNodeTypeFor(tinyusdz::tydra::NodeType type); +aiMatrix4x4 tinyUsdzMat4ToAiMat4(const double matIn[4][4]); + +aiVector3D tinyUsdzScaleOrPosToAssimp(const std::array &scaleOrPosIn); + +/** + * Convert quaternion from tinyusdz "quat" to assimp "aiQuaternion" type + * + * @param quatIn tinyusdz float[4] in x,y,z,w order + * @return assimp aiQuaternion converted from input + */ +aiQuaternion tinyUsdzQuatToAiQuat(const std::array &quatIn); + +} // namespace Assimp +#endif // AI_USDLOADER_IMPL_TINYUSDZ_HELPER_H_INCLUDED diff --git a/code/AssetLib/USD/USDLoaderUtil.cpp b/code/AssetLib/USD/USDLoaderUtil.cpp new file mode 100644 index 000000000..8d9b22df2 --- /dev/null +++ b/code/AssetLib/USD/USDLoaderUtil.cpp @@ -0,0 +1,116 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2024, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file USDLoader.cpp + * @brief Implementation of the USD importer class + */ + +#ifndef ASSIMP_BUILD_NO_USD_IMPORTER +#include + +// internal headers +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "USDLoaderUtil.h" + +namespace Assimp { +using namespace std; +bool isUsda(const std::string &pFile) { + size_t pos = pFile.find_last_of('.'); + if (pos == string::npos) { + return false; + } + string ext = pFile.substr(pos + 1); + if (ext.size() != 4) { + return false; + } + return (ext[0] == 'u' || ext[0] == 'U') && (ext[1] == 's' || ext[1] == 'S') && (ext[2] == 'd' || ext[2] == 'D') && (ext[3] == 'a' || ext[3] == 'A'); +} + +bool isUsdc(const std::string &pFile) { + size_t pos = pFile.find_last_of('.'); + if (pos == string::npos) { + return false; + } + string ext = pFile.substr(pos + 1); + if (ext.size() != 4) { + return false; + } + return (ext[0] == 'u' || ext[0] == 'U') && (ext[1] == 's' || ext[1] == 'S') && (ext[2] == 'd' || ext[2] == 'D') && (ext[3] == 'c' || ext[3] == 'C'); +} + +bool isUsdz(const std::string &pFile) { + size_t pos = pFile.find_last_of('.'); + if (pos == string::npos) { + return false; + } + string ext = pFile.substr(pos + 1); + if (ext.size() != 4) { + return false; + } + return (ext[0] == 'u' || ext[0] == 'U') && (ext[1] == 's' || ext[1] == 'S') && (ext[2] == 'd' || ext[2] == 'D') && (ext[3] == 'z' || ext[3] == 'Z'); +} + +bool isUsd(const std::string &pFile) { + size_t pos = pFile.find_last_of('.'); + if (pos == string::npos) { + return false; + } + string ext = pFile.substr(pos + 1); + if (ext.size() != 3) { + return false; + } + return (ext[0] == 'u' || ext[0] == 'U') && (ext[1] == 's' || ext[1] == 'S') && (ext[2] == 'd' || ext[2] == 'D'); +} +} // namespace Assimp + +#endif // !! ASSIMP_BUILD_NO_USD_IMPORTER diff --git a/code/AssetLib/USD/USDLoaderUtil.h b/code/AssetLib/USD/USDLoaderUtil.h new file mode 100644 index 000000000..7601cfbc1 --- /dev/null +++ b/code/AssetLib/USD/USDLoaderUtil.h @@ -0,0 +1,60 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2024, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file USDLoader.h + * @brief Declaration of the USD importer class. + */ +#pragma once +#ifndef AI_USDLOADER_UTIL_H_INCLUDED +#define AI_USDLOADER_UTIL_H_INCLUDED + +#include + +namespace Assimp { + +bool isUsda(const std::string &pFile); +bool isUsdc(const std::string &pFile); +bool isUsdz(const std::string &pFile); +bool isUsd(const std::string &pFile); + +} // namespace Assimp + +#endif // AI_USDLOADER_UTIL_H_INCLUDED diff --git a/code/AssetLib/USD/USDPreprocessor.h b/code/AssetLib/USD/USDPreprocessor.h new file mode 100644 index 000000000..dec37ea27 --- /dev/null +++ b/code/AssetLib/USD/USDPreprocessor.h @@ -0,0 +1,50 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2024, assimp team + + All rights reserved. + + Redistribution and use of this software in source and binary forms, + with or without modification, are permitted provided that the + following conditions are met: + + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + + * Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ---------------------------------------------------------------------- + */ + +/** @file USDLoader.h + * @brief Declaration of the USD importer class. + */ +#pragma once +#ifndef AI_USDPREPROCESSOR_H_INCLUDED +#define AI_USDPREPROCESSOR_H_INCLUDED + +#define UNUSED(x) (void) x + +#endif // AI_USDPREPROCESSOR_H_INCLUDED diff --git a/code/AssetLib/Unreal/UnrealLoader.cpp b/code/AssetLib/Unreal/UnrealLoader.cpp index 3810e5bf4..85b68b508 100644 --- a/code/AssetLib/Unreal/UnrealLoader.cpp +++ b/code/AssetLib/Unreal/UnrealLoader.cpp @@ -452,7 +452,7 @@ void UnrealImporter::InternReadFile(const std::string &pFile, aiColor3D color(1.f, 1.f, 1.f); aiString s; - ::ai_snprintf(s.data, MAXLEN, "mat%u_tx%u_", i, materials[i].tex); + ::ai_snprintf(s.data, AI_MAXLEN, "mat%u_tx%u_", i, materials[i].tex); // set the two-sided flag if (materials[i].type == Unreal::MF_NORMAL_TS) { @@ -472,7 +472,7 @@ void UnrealImporter::InternReadFile(const std::string &pFile, // a special name for the weapon attachment point if (materials[i].type == Unreal::MF_WEAPON_PLACEHOLDER) { - s.length = ::ai_snprintf(s.data, MAXLEN, "$WeaponTag$"); + s.length = ::ai_snprintf(s.data, AI_MAXLEN, "$WeaponTag$"); color = aiColor3D(0.f, 0.f, 0.f); } diff --git a/code/AssetLib/glTF/glTFImporter.cpp b/code/AssetLib/glTF/glTFImporter.cpp index 1d718fd20..2443205f3 100644 --- a/code/AssetLib/glTF/glTFImporter.cpp +++ b/code/AssetLib/glTF/glTFImporter.cpp @@ -109,7 +109,7 @@ inline void SetMaterialColorProperty(std::vector &embeddedTexIdxs, Asset & if (texIdx != -1) { // embedded // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture) uri.data[0] = '*'; - uri.length = 1 + ASSIMP_itoa10(uri.data + 1, MAXLEN - 1, texIdx); + uri.length = 1 + ASSIMP_itoa10(uri.data + 1, AI_MAXLEN - 1, texIdx); } mat->AddProperty(&uri, _AI_MATKEY_TEXTURE_BASE, texType, 0); @@ -242,7 +242,7 @@ void glTFImporter::ImportMeshes(glTF::Asset &r) { if (mesh.primitives.size() > 1) { ai_uint32 &len = aim->mName.length; aim->mName.data[len] = '-'; - len += 1 + ASSIMP_itoa10(aim->mName.data + len + 1, unsigned(MAXLEN - len - 1), p); + len += 1 + ASSIMP_itoa10(aim->mName.data + len + 1, unsigned(AI_MAXLEN - len - 1), p); } switch (prim.mode) { diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 457fee8fa..3ae5e48b1 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -1036,10 +1036,10 @@ size_t Accessor::ExtractData(T *&outData, const std::vector *remap outData = new T[usedCount]; if (remappingIndices != nullptr) { - const unsigned int maxIndex = static_cast(maxSize / stride - 1); + const unsigned int maxIndexCount = static_cast(maxSize / stride); for (size_t i = 0; i < usedCount; ++i) { size_t srcIdx = (*remappingIndices)[i]; - if (srcIdx > maxIndex) { + if (srcIdx >= maxIndexCount) { throw DeadlyImportError("GLTF: index*stride ", (srcIdx * stride), " > maxSize ", maxSize, " in ", getContextForErrorMessages(id, name)); } memcpy(outData + i, data + srcIdx * stride, elemSize); @@ -1440,7 +1440,7 @@ inline void MaterialSheen::SetDefaults() { inline void MaterialVolume::SetDefaults() { //KHR_materials_volume properties thicknessFactor = 0.f; - attenuationDistance = INFINITY; + attenuationDistance = std::numeric_limits::infinity(); SetVector(attenuationColor, defaultAttenuationColor); } diff --git a/code/AssetLib/glTF2/glTF2AssetWriter.inl b/code/AssetLib/glTF2/glTF2AssetWriter.inl index 17e29d8e7..3e42ffe57 100644 --- a/code/AssetLib/glTF2/glTF2AssetWriter.inl +++ b/code/AssetLib/glTF2/glTF2AssetWriter.inl @@ -507,7 +507,7 @@ namespace glTF2 { WriteTex(materialVolume, volume.thicknessTexture, "thicknessTexture", w.mAl); - if (volume.attenuationDistance != INFINITY) { + if (volume.attenuationDistance != std::numeric_limits::infinity()) { WriteFloat(materialVolume, volume.attenuationDistance, "attenuationDistance", w.mAl); } diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index 5f895ff74..eb158ce4f 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -92,7 +92,7 @@ static constexpr aiImporterDesc desc = { 0, 0, 0, - "gltf glb" + "gltf glb vrm" }; glTF2Importer::glTF2Importer() : @@ -106,7 +106,7 @@ const aiImporterDesc *glTF2Importer::GetInfo() const { bool glTF2Importer::CanRead(const std::string &filename, IOSystem *pIOHandler, bool checkSig) const { const std::string extension = GetExtension(filename); - if (!checkSig && (extension != "gltf") && (extension != "glb")) { + if (!checkSig && (extension != "gltf") && (extension != "glb") && (extension != "vrm")) { return false; } @@ -161,7 +161,7 @@ static void SetMaterialTextureProperty(std::vector &embeddedTexIdxs, Asset if (texIdx != -1) { // embedded // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture) uri.data[0] = '*'; - uri.length = 1 + ASSIMP_itoa10(uri.data + 1, MAXLEN - 1, texIdx); + uri.length = 1 + ASSIMP_itoa10(uri.data + 1, AI_MAXLEN - 1, texIdx); } mat->AddProperty(&uri, AI_MATKEY_TEXTURE(texType, texSlot)); @@ -539,7 +539,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { if (mesh.primitives.size() > 1) { ai_uint32 &len = aim->mName.length; aim->mName.data[len] = '-'; - len += 1 + ASSIMP_itoa10(aim->mName.data + len + 1, unsigned(MAXLEN - len - 1), p); + len += 1 + ASSIMP_itoa10(aim->mName.data + len + 1, unsigned(AI_MAXLEN - len - 1), p); } switch (prim.mode) { diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index a521c9fb8..30730f716 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -471,6 +471,14 @@ ADD_ASSIMP_IMPORTER( MDL AssetLib/MDL/HalfLife/UniqueNameGenerator.h ) +IF(((CMAKE_CXX_COMPILER_ID MATCHES "GNU") AND NOT MINGW AND NOT HAIKU) AND CMAKE_CXX_COMPILER_VERSION GREATER_EQUAL 13) + message(STATUS "GCC13 detected disabling \"-Warray-bounds and -Wstringop-overflow\" for" + " AssetLib/MDL/MDLLoader.cpp as it appears to be a false positive") + set_source_files_properties(AssetLib/MDL/MDLLoader.cpp PROPERTIES + COMPILE_FLAGS "-Wno-array-bounds -Wno-stringop-overflow" + ) +endif() + SET( MaterialSystem_SRCS Material/MaterialSystem.cpp Material/MaterialSystem.h @@ -503,6 +511,14 @@ ADD_ASSIMP_IMPORTER( OBJ AssetLib/Obj/ObjTools.h ) +IF(((CMAKE_CXX_COMPILER_ID MATCHES "GNU") AND NOT MINGW AND NOT HAIKU) AND CMAKE_CXX_COMPILER_VERSION EQUAL 14) + message(STATUS "GCC14 detected disabling \"-Wmaybe-uninitialized\" for" + " AssetLib/Obj/ObjFileParser.cpp as it appears to be a false positive") + set_source_files_properties(AssetLib/Obj/ObjFileParser.cpp PROPERTIES + COMPILE_FLAGS "-Wno-maybe-uninitialized" + ) +endif() + ADD_ASSIMP_IMPORTER( OGRE AssetLib/Ogre/OgreImporter.h AssetLib/Ogre/OgreStructs.h @@ -812,6 +828,17 @@ ADD_ASSIMP_IMPORTER( 3D AssetLib/Unreal/UnrealLoader.h ) +ADD_ASSIMP_IMPORTER( USD + AssetLib/USD/USDLoader.cpp + AssetLib/USD/USDLoader.h + AssetLib/USD/USDLoaderImplTinyusdz.cpp + AssetLib/USD/USDLoaderImplTinyusdz.h + AssetLib/USD/USDLoaderImplTinyusdzHelper.cpp + AssetLib/USD/USDLoaderImplTinyusdzHelper.h + AssetLib/USD/USDLoaderUtil.cpp + AssetLib/USD/USDLoaderUtil.h +) + ADD_ASSIMP_IMPORTER( X AssetLib/X/XFileHelper.h AssetLib/X/XFileImporter.cpp @@ -905,6 +932,123 @@ SET( Extra_SRCS ) SOURCE_GROUP( Extra FILES ${Extra_SRCS}) +# USD/USDA/USDC/USDZ support +# tinyusdz +IF (ASSIMP_BUILD_USD_IMPORTER) + if (ASSIMP_BUILD_USD_VERBOSE_LOGS) + ADD_DEFINITIONS( -DASSIMP_USD_VERBOSE_LOGS) + endif () + # Use CMAKE_CURRENT_SOURCE_DIR which provides assimp-local path (CMAKE_SOURCE_DIR is + # relative to top-level/main project) + set(Tinyusdz_BASE_ABSPATH "${CMAKE_CURRENT_SOURCE_DIR}/../contrib/tinyusdz") + set(Tinyusdz_REPO_ABSPATH "${Tinyusdz_BASE_ABSPATH}/autoclone") + + # Note: ALWAYS specify a git commit hash (or tag) instead of a branch name; using a branch + # name can lead to non-deterministic (unpredictable) results since the code is potentially + # in flux + # "dev" branch, 9 Jul 2024 + set(TINYUSDZ_GIT_TAG "bd2a1edbbf69f352a6c40730114db9918c384848") + message("****") + message("\n\n**** Cloning tinyusdz repo, git tag ${TINYUSDZ_GIT_TAG}\n\n") + + # Patch required to build arm32 on android + set(TINYUSDZ_PATCH_CMD git apply ${Tinyusdz_BASE_ABSPATH}/patches/tinyusdz.patch) + + # Note: CMake's "FetchContent" (which executes at configure time) is much better for this use case + # than "ExternalProject" (which executes at build time); we just want to clone a repo and + # block (wait) as long as necessary until cloning is complete, so we immediately have full + # access to the cloned source files + include(FetchContent) + # Only want to clone once (on Android, using SOURCE_DIR will clone per-ABI (x86, x86_64 etc)) + set(FETCHCONTENT_BASE_DIR ${Tinyusdz_REPO_ABSPATH}) + set(FETCHCONTENT_QUIET on) # Turn off to troubleshoot repo clone problems + set(FETCHCONTENT_UPDATES_DISCONNECTED on) # Prevent other ABIs from re-cloning/re-patching etc + FetchContent_Declare( + tinyusdz_repo + GIT_REPOSITORY "https://github.com/lighttransport/tinyusdz" + GIT_TAG ${TINYUSDZ_GIT_TAG} + PATCH_COMMAND ${TINYUSDZ_PATCH_CMD} + ) + FetchContent_MakeAvailable(tinyusdz_repo) + message("**** Finished cloning tinyusdz repo") + message("****") + + set(Tinyusdz_SRC_ABSPATH "${Tinyusdz_REPO_ABSPATH}/tinyusdz_repo-src/src") + set(Tinyusdz_SRCS + ${Tinyusdz_SRC_ABSPATH}/ascii-parser.cc + ${Tinyusdz_SRC_ABSPATH}/ascii-parser-basetype.cc + ${Tinyusdz_SRC_ABSPATH}/ascii-parser-timesamples.cc + ${Tinyusdz_SRC_ABSPATH}/ascii-parser-timesamples-array.cc + ${Tinyusdz_SRC_ABSPATH}/asset-resolution.cc + ${Tinyusdz_SRC_ABSPATH}/composition.cc + ${Tinyusdz_SRC_ABSPATH}/crate-format.cc + ${Tinyusdz_SRC_ABSPATH}/crate-pprint.cc + ${Tinyusdz_SRC_ABSPATH}/crate-reader.cc + ${Tinyusdz_SRC_ABSPATH}/image-loader.cc + ${Tinyusdz_SRC_ABSPATH}/image-util.cc + ${Tinyusdz_SRC_ABSPATH}/image-writer.cc + ${Tinyusdz_SRC_ABSPATH}/io-util.cc + ${Tinyusdz_SRC_ABSPATH}/linear-algebra.cc + ${Tinyusdz_SRC_ABSPATH}/path-util.cc + ${Tinyusdz_SRC_ABSPATH}/pprinter.cc + ${Tinyusdz_SRC_ABSPATH}/prim-composition.cc + ${Tinyusdz_SRC_ABSPATH}/prim-reconstruct.cc + ${Tinyusdz_SRC_ABSPATH}/prim-types.cc + ${Tinyusdz_SRC_ABSPATH}/primvar.cc + ${Tinyusdz_SRC_ABSPATH}/stage.cc + ${Tinyusdz_SRC_ABSPATH}/str-util.cc + ${Tinyusdz_SRC_ABSPATH}/tiny-format.cc + ${Tinyusdz_SRC_ABSPATH}/tinyusdz.cc + ${Tinyusdz_SRC_ABSPATH}/tydra/attribute-eval.cc + ${Tinyusdz_SRC_ABSPATH}/tydra/attribute-eval-typed.cc + ${Tinyusdz_SRC_ABSPATH}/tydra/attribute-eval-typed-animatable.cc + ${Tinyusdz_SRC_ABSPATH}/tydra/attribute-eval-typed-animatable-fallback.cc + ${Tinyusdz_SRC_ABSPATH}/tydra/attribute-eval-typed-fallback.cc + ${Tinyusdz_SRC_ABSPATH}/tydra/facial.cc + ${Tinyusdz_SRC_ABSPATH}/tydra/prim-apply.cc + ${Tinyusdz_SRC_ABSPATH}/tydra/render-data.cc + ${Tinyusdz_SRC_ABSPATH}/tydra/scene-access.cc + ${Tinyusdz_SRC_ABSPATH}/tydra/shader-network.cc + ${Tinyusdz_SRC_ABSPATH}/usda-reader.cc + ${Tinyusdz_SRC_ABSPATH}/usda-writer.cc + ${Tinyusdz_SRC_ABSPATH}/usdc-reader.cc + ${Tinyusdz_SRC_ABSPATH}/usdc-writer.cc + ${Tinyusdz_SRC_ABSPATH}/usdGeom.cc + ${Tinyusdz_SRC_ABSPATH}/usdLux.cc + ${Tinyusdz_SRC_ABSPATH}/usdMtlx.cc + ${Tinyusdz_SRC_ABSPATH}/usdShade.cc + ${Tinyusdz_SRC_ABSPATH}/usdSkel.cc + ${Tinyusdz_SRC_ABSPATH}/value-pprint.cc + ${Tinyusdz_SRC_ABSPATH}/value-types.cc + ${Tinyusdz_SRC_ABSPATH}/xform.cc + ) + + set(Tinyusdz_DEP_SOURCES + ${Tinyusdz_SRC_ABSPATH}/external/fpng.cpp + #${Tinyusdz_SRC_ABSPATH}/external/staticstruct.cc + #${Tinyusdz_SRC_ABSPATH}/external/string_id/database.cpp + #${Tinyusdz_SRC_ABSPATH}/external/string_id/error.cpp + #${Tinyusdz_SRC_ABSPATH}/external/string_id/string_id.cpp + #${Tinyusdz_SRC_ABSPATH}/external/tinyxml2/tinyxml2.cpp + ${Tinyusdz_SRC_ABSPATH}/integerCoding.cpp + ${Tinyusdz_SRC_ABSPATH}/lz4-compression.cc + ${Tinyusdz_SRC_ABSPATH}/lz4/lz4.c + ) + + set(tinyusdz_INCLUDE_DIRS "${Tinyusdz_SRC_ABSPATH}") + INCLUDE_DIRECTORIES(${tinyusdz_INCLUDE_DIRS}) + SOURCE_GROUP( Contrib\\Tinyusdz + FILES + ${Tinyusdz_SRCS} + ${Tinyusdz_DEP_SOURCES} + ) + MESSAGE(STATUS "tinyusdz enabled") +ELSE() # IF (ASSIMP_BUILD_USD_IMPORTER) + set(Tinyusdz_SRCS "") + set(Tinyusdz_DEP_SOURCES "") + MESSAGE(STATUS "tinyusdz disabled") +ENDIF() # IF (ASSIMP_BUILD_USD_IMPORTER) + # pugixml IF(ASSIMP_HUNTER_ENABLED) hunter_add_package(pugixml) @@ -1155,6 +1299,8 @@ SET( assimp_src ${openddl_parser_SRCS} ${open3dgc_SRCS} ${ziplib_SRCS} + ${Tinyusdz_SRCS} + ${Tinyusdz_DEP_SOURCES} ${Pugixml_SRCS} ${stb_SRCS} # Necessary to show the headers in the project when using the VC++ generator: @@ -1188,7 +1334,7 @@ ADD_LIBRARY(assimp::assimp ALIAS assimp) IF (BUILD_SHARED_LIBS) TARGET_COMPILE_DEFINITIONS(assimp PRIVATE ASSIMP_BUILD_DLL_EXPORT) ELSE () - TARGET_COMPILE_DEFINITIONS(assimp PRIVATE OPENDDL_STATIC_LIBARY) + TARGET_COMPILE_DEFINITIONS(assimp PRIVATE OPENDDL_STATIC_LIBARY P2T_STATIC_EXPORTS) ENDIF () TARGET_USE_COMMON_OUTPUT_DIRECTORY(assimp) @@ -1400,19 +1546,20 @@ IF (RT_FOUND AND ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC) TARGET_LINK_LIBRARIES(assimp rt) ENDIF () +IF(ASSIMP_INSTALL) + INSTALL( TARGETS assimp + EXPORT "${TARGETS_EXPORT_NAME}" + LIBRARY DESTINATION ${ASSIMP_LIB_INSTALL_DIR} COMPONENT ${LIBASSIMP_COMPONENT} + ARCHIVE DESTINATION ${ASSIMP_LIB_INSTALL_DIR} COMPONENT ${LIBASSIMP-DEV_COMPONENT} + RUNTIME DESTINATION ${ASSIMP_BIN_INSTALL_DIR} COMPONENT ${LIBASSIMP_COMPONENT} + FRAMEWORK DESTINATION ${ASSIMP_LIB_INSTALL_DIR} COMPONENT ${LIBASSIMP_COMPONENT} + INCLUDES DESTINATION ${ASSIMP_INCLUDE_INSTALL_DIR} + ) + INSTALL( FILES ${PUBLIC_HEADERS} DESTINATION ${ASSIMP_INCLUDE_INSTALL_DIR}/assimp COMPONENT assimp-dev) + INSTALL( FILES ${COMPILER_HEADERS} DESTINATION ${ASSIMP_INCLUDE_INSTALL_DIR}/assimp/Compiler COMPONENT assimp-dev) +ENDIF() -INSTALL( TARGETS assimp - EXPORT "${TARGETS_EXPORT_NAME}" - LIBRARY DESTINATION ${ASSIMP_LIB_INSTALL_DIR} COMPONENT ${LIBASSIMP_COMPONENT} - ARCHIVE DESTINATION ${ASSIMP_LIB_INSTALL_DIR} COMPONENT ${LIBASSIMP-DEV_COMPONENT} - RUNTIME DESTINATION ${ASSIMP_BIN_INSTALL_DIR} COMPONENT ${LIBASSIMP_COMPONENT} - FRAMEWORK DESTINATION ${ASSIMP_LIB_INSTALL_DIR} COMPONENT ${LIBASSIMP_COMPONENT} - INCLUDES DESTINATION ${ASSIMP_INCLUDE_INSTALL_DIR} -) -INSTALL( FILES ${PUBLIC_HEADERS} DESTINATION ${ASSIMP_INCLUDE_INSTALL_DIR}/assimp COMPONENT assimp-dev) -INSTALL( FILES ${COMPILER_HEADERS} DESTINATION ${ASSIMP_INCLUDE_INSTALL_DIR}/assimp/Compiler COMPONENT assimp-dev) - -if (ASSIMP_ANDROID_JNIIOSYSTEM) +if (ASSIMP_ANDROID_JNIIOSYSTEM AND ASSIMP_INSTALL) INSTALL(FILES ${HEADER_PATH}/${ASSIMP_ANDROID_JNIIOSYSTEM_PATH}/AndroidJNIIOSystem.h DESTINATION ${ASSIMP_INCLUDE_INSTALL_DIR} COMPONENT assimp-dev) diff --git a/code/Common/Assimp.cpp b/code/Common/Assimp.cpp index 36a3c962e..ef3ee7b5d 100644 --- a/code/Common/Assimp.cpp +++ b/code/Common/Assimp.cpp @@ -233,8 +233,13 @@ const aiScene *aiImportFileFromMemoryWithProperties( unsigned int pFlags, const char *pHint, const aiPropertyStore *props) { - ai_assert(nullptr != pBuffer); - ai_assert(0 != pLength); + if (pBuffer == nullptr) { + return nullptr; + } + + if (pLength == 0u) { + return nullptr; + } const aiScene *scene = nullptr; ASSIMP_BEGIN_EXCEPTION_REGION(); @@ -743,14 +748,14 @@ ASSIMP_API void aiVector2DivideByVector( } // ------------------------------------------------------------------------------------------------ -ASSIMP_API float aiVector2Length( +ASSIMP_API ai_real aiVector2Length( const C_STRUCT aiVector2D *v) { ai_assert(nullptr != v); return v->Length(); } // ------------------------------------------------------------------------------------------------ -ASSIMP_API float aiVector2SquareLength( +ASSIMP_API ai_real aiVector2SquareLength( const C_STRUCT aiVector2D *v) { ai_assert(nullptr != v); return v->SquareLength(); @@ -764,7 +769,7 @@ ASSIMP_API void aiVector2Negate( } // ------------------------------------------------------------------------------------------------ -ASSIMP_API float aiVector2DotProduct( +ASSIMP_API ai_real aiVector2DotProduct( const C_STRUCT aiVector2D *a, const C_STRUCT aiVector2D *b) { ai_assert(nullptr != a); @@ -859,14 +864,14 @@ ASSIMP_API void aiVector3DivideByVector( } // ------------------------------------------------------------------------------------------------ -ASSIMP_API float aiVector3Length( +ASSIMP_API ai_real aiVector3Length( const C_STRUCT aiVector3D *v) { ai_assert(nullptr != v); return v->Length(); } // ------------------------------------------------------------------------------------------------ -ASSIMP_API float aiVector3SquareLength( +ASSIMP_API ai_real aiVector3SquareLength( const C_STRUCT aiVector3D *v) { ai_assert(nullptr != v); return v->SquareLength(); @@ -880,7 +885,7 @@ ASSIMP_API void aiVector3Negate( } // ------------------------------------------------------------------------------------------------ -ASSIMP_API float aiVector3DotProduct( +ASSIMP_API ai_real aiVector3DotProduct( const C_STRUCT aiVector3D *a, const C_STRUCT aiVector3D *b) { ai_assert(nullptr != a); @@ -966,7 +971,7 @@ ASSIMP_API void aiMatrix3Inverse(C_STRUCT aiMatrix3x3 *mat) { } // ------------------------------------------------------------------------------------------------ -ASSIMP_API float aiMatrix3Determinant(const C_STRUCT aiMatrix3x3 *mat) { +ASSIMP_API ai_real aiMatrix3Determinant(const C_STRUCT aiMatrix3x3 *mat) { ai_assert(nullptr != mat); return mat->Determinant(); } @@ -1066,7 +1071,7 @@ ASSIMP_API void aiMatrix4Inverse(C_STRUCT aiMatrix4x4 *mat) { } // ------------------------------------------------------------------------------------------------ -ASSIMP_API float aiMatrix4Determinant(const C_STRUCT aiMatrix4x4 *mat) { +ASSIMP_API ai_real aiMatrix4Determinant(const C_STRUCT aiMatrix4x4 *mat) { ai_assert(nullptr != mat); return mat->Determinant(); } diff --git a/code/Common/BaseImporter.cpp b/code/Common/BaseImporter.cpp index 3a4c7c329..5c70cc27e 100644 --- a/code/Common/BaseImporter.cpp +++ b/code/Common/BaseImporter.cpp @@ -250,9 +250,10 @@ void BaseImporter::GetExtensionList(std::set &extensions) { /*static*/ bool BaseImporter::SimpleExtensionCheck(const std::string &pFile, const char *ext0, const char *ext1, - const char *ext2) { + const char *ext2, + const char *ext3) { std::set extensions; - for (const char* ext : {ext0, ext1, ext2}) { + for (const char* ext : {ext0, ext1, ext2, ext3}) { if (ext == nullptr) continue; extensions.emplace(ext); } diff --git a/code/Common/Bitmap.cpp b/code/Common/Bitmap.cpp index 246ad9e65..e6732c528 100644 --- a/code/Common/Bitmap.cpp +++ b/code/Common/Bitmap.cpp @@ -148,8 +148,10 @@ void Bitmap::WriteData(aiTexture *texture, IOStream *file) { file->Write(pixel, mBytesPerPixel, 1); } - - file->Write(padding_data, padding, 1); + // When padding is 0, passing it as an argument will cause an assertion failure in DefaultIOStream::Write. + if (padding) { + file->Write(padding_data, padding, 1); + } } } diff --git a/code/Common/DefaultIOSystem.cpp b/code/Common/DefaultIOSystem.cpp index e74add55f..ddb5b3b60 100644 --- a/code/Common/DefaultIOSystem.cpp +++ b/code/Common/DefaultIOSystem.cpp @@ -93,6 +93,10 @@ static std::string WideToUtf8(const wchar_t *in) { // ------------------------------------------------------------------------------------------------ // Tests for the existence of a file at the given path. bool DefaultIOSystem::Exists(const char *pFile) const { + if (pFile == nullptr) { + return false; + } + #ifdef _WIN32 struct __stat64 filestat; if (_wstat64(Utf8ToWide(pFile).c_str(), &filestat) != 0) { diff --git a/code/Common/ImporterRegistry.cpp b/code/Common/ImporterRegistry.cpp index 92a04e692..276f20974 100644 --- a/code/Common/ImporterRegistry.cpp +++ b/code/Common/ImporterRegistry.cpp @@ -55,6 +55,9 @@ corresponding preprocessor flag to selectively disable formats. // Importers // (include_new_importers_here) // ------------------------------------------------------------------------------------------------ +#if !defined(ASSIMP_BUILD_NO_USD_IMPORTER) +#include "AssetLib/USD/USDLoader.h" +#endif #ifndef ASSIMP_BUILD_NO_X_IMPORTER #include "AssetLib/X/XFileImporter.h" #endif @@ -230,6 +233,9 @@ void GetImporterInstanceList(std::vector &out) { // (register_new_importers_here) // ---------------------------------------------------------------------------- out.reserve(64); +#if !defined(ASSIMP_BUILD_NO_USD_IMPORTER) + out.push_back(new USDImporter()); +#endif #if (!defined ASSIMP_BUILD_NO_X_IMPORTER) out.push_back(new XFileImporter()); #endif diff --git a/code/Common/SceneCombiner.cpp b/code/Common/SceneCombiner.cpp index 44897a40b..7954d4d01 100644 --- a/code/Common/SceneCombiner.cpp +++ b/code/Common/SceneCombiner.cpp @@ -78,7 +78,7 @@ inline void PrefixString(aiString &string, const char *prefix, unsigned int len) if (string.length >= 1 && string.data[0] == '$') return; - if (len + string.length >= MAXLEN - 1) { + if (len + string.length >= AI_MAXLEN - 1) { ASSIMP_LOG_VERBOSE_DEBUG("Can't add an unique prefix because the string is too long"); ai_assert(false); return; @@ -408,7 +408,7 @@ void SceneCombiner::MergeScenes(aiScene **_dest, aiScene *master, std::vectormData. The size of mData is not guaranteed to be - // MAXLEN in size. + // AI_MAXLEN in size. aiString s(*(aiString *)prop->mData); if ('*' == s.data[0]) { // Offset the index and write it back .. diff --git a/code/Common/SpatialSort.cpp b/code/Common/SpatialSort.cpp index 34cb3fb05..6bce63af4 100644 --- a/code/Common/SpatialSort.cpp +++ b/code/Common/SpatialSort.cpp @@ -71,10 +71,6 @@ SpatialSort::SpatialSort() : mPlaneNormal.Normalize(); } -// ------------------------------------------------------------------------------------------------ -// Destructor -SpatialSort::~SpatialSort() = default; - // ------------------------------------------------------------------------------------------------ void SpatialSort::Fill(const aiVector3D *pPositions, unsigned int pNumPositions, unsigned int pElementOffset, diff --git a/code/Common/StackAllocator.h b/code/Common/StackAllocator.h index a5d97c22d..fcb72a09e 100644 --- a/code/Common/StackAllocator.h +++ b/code/Common/StackAllocator.h @@ -88,6 +88,11 @@ private: } // namespace Assimp +/// @brief Fixes an undefined reference error when linking in certain build environments. +// May throw warnings about needing stdc++17, but should compile without issues on modern compilers. +inline const size_t Assimp::StackAllocator::g_maxBytesPerBlock; +inline const size_t Assimp::StackAllocator::g_startBytesPerBlock; + #include "StackAllocator.inl" #endif // include guard diff --git a/code/Common/Subdivision.cpp b/code/Common/Subdivision.cpp index fc8ab2099..78c249807 100644 --- a/code/Common/Subdivision.cpp +++ b/code/Common/Subdivision.cpp @@ -524,7 +524,11 @@ void CatmullClarkSubdivider::InternSubdivide( } } - ai_assert(adj[o] - moffsets[nidx].first < mp->mNumFaces); + if (mp == nullptr) { + continue; + } + + ai_assert(adj[o] - moffsets[nidx].first < mp->mNumFaces); const aiFace &f = mp->mFaces[adj[o] - moffsets[nidx].first]; bool haveit = false; diff --git a/code/Common/Version.cpp b/code/Common/Version.cpp index 10ccf58e7..2fca44824 100644 --- a/code/Common/Version.cpp +++ b/code/Common/Version.cpp @@ -44,8 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "ScenePrivate.h" #include #include - -#include "revision.h" +#include // -------------------------------------------------------------------------------- // Legal information string - don't remove this. @@ -118,83 +117,3 @@ ASSIMP_API const char *aiGetBranchName() { return GitBranch; } -// ------------------------------------------------------------------------------------------------ -ASSIMP_API aiScene::aiScene() : - mFlags(0), - mRootNode(nullptr), - mNumMeshes(0), - mMeshes(nullptr), - mNumMaterials(0), - mMaterials(nullptr), - mNumAnimations(0), - mAnimations(nullptr), - mNumTextures(0), - mTextures(nullptr), - mNumLights(0), - mLights(nullptr), - mNumCameras(0), - mCameras(nullptr), - mMetaData(nullptr), - mName(), - mNumSkeletons(0), - mSkeletons(nullptr), - mPrivate(new Assimp::ScenePrivateData()) { - // empty -} - -// ------------------------------------------------------------------------------------------------ -ASSIMP_API aiScene::~aiScene() { - // delete all sub-objects recursively - delete mRootNode; - - // To make sure we won't crash if the data is invalid it's - // much better to check whether both mNumXXX and mXXX are - // valid instead of relying on just one of them. - if (mNumMeshes && mMeshes) { - for (unsigned int a = 0; a < mNumMeshes; ++a) { - delete mMeshes[a]; - } - } - delete[] mMeshes; - - if (mNumMaterials && mMaterials) { - for (unsigned int a = 0; a < mNumMaterials; ++a) { - delete mMaterials[a]; - } - } - delete[] mMaterials; - - if (mNumAnimations && mAnimations) { - for (unsigned int a = 0; a < mNumAnimations; ++a) { - delete mAnimations[a]; - } - } - delete[] mAnimations; - - if (mNumTextures && mTextures) { - for (unsigned int a = 0; a < mNumTextures; ++a) { - delete mTextures[a]; - } - } - delete[] mTextures; - - if (mNumLights && mLights) { - for (unsigned int a = 0; a < mNumLights; ++a) { - delete mLights[a]; - } - } - delete[] mLights; - - if (mNumCameras && mCameras) { - for (unsigned int a = 0; a < mNumCameras; ++a) { - delete mCameras[a]; - } - } - delete[] mCameras; - - aiMetadata::Dealloc(mMetaData); - - delete[] mSkeletons; - - delete static_cast(mPrivate); -} diff --git a/code/Common/scene.cpp b/code/Common/scene.cpp index e42f4b2e6..5c2e370d2 100644 --- a/code/Common/scene.cpp +++ b/code/Common/scene.cpp @@ -40,6 +40,87 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include +#include "ScenePrivate.h" + +aiScene::aiScene() : + mFlags(0), + mRootNode(nullptr), + mNumMeshes(0), + mMeshes(nullptr), + mNumMaterials(0), + mMaterials(nullptr), + mNumAnimations(0), + mAnimations(nullptr), + mNumTextures(0), + mTextures(nullptr), + mNumLights(0), + mLights(nullptr), + mNumCameras(0), + mCameras(nullptr), + mMetaData(nullptr), + mName(), + mNumSkeletons(0), + mSkeletons(nullptr), + mPrivate(new Assimp::ScenePrivateData()) { + // empty +} + +aiScene::~aiScene() { + // delete all sub-objects recursively + delete mRootNode; + + // To make sure we won't crash if the data is invalid it's + // much better to check whether both mNumXXX and mXXX are + // valid instead of relying on just one of them. + if (mNumMeshes && mMeshes) { + for (unsigned int a = 0; a < mNumMeshes; ++a) { + delete mMeshes[a]; + } + } + delete[] mMeshes; + + if (mNumMaterials && mMaterials) { + for (unsigned int a = 0; a < mNumMaterials; ++a) { + delete mMaterials[a]; + } + } + delete[] mMaterials; + + if (mNumAnimations && mAnimations) { + for (unsigned int a = 0; a < mNumAnimations; ++a) { + delete mAnimations[a]; + } + } + delete[] mAnimations; + + if (mNumTextures && mTextures) { + for (unsigned int a = 0; a < mNumTextures; ++a) { + delete mTextures[a]; + } + } + delete[] mTextures; + + if (mNumLights && mLights) { + for (unsigned int a = 0; a < mNumLights; ++a) { + delete mLights[a]; + } + } + delete[] mLights; + + if (mNumCameras && mCameras) { + for (unsigned int a = 0; a < mNumCameras; ++a) { + delete mCameras[a]; + } + } + delete[] mCameras; + + aiMetadata::Dealloc(mMetaData); + + delete[] mSkeletons; + + delete static_cast(mPrivate); +} + aiNode::aiNode() : mName(""), mParent(nullptr), diff --git a/code/Geometry/GeometryUtils.h b/code/Geometry/GeometryUtils.h index 2e205c872..ae88d376b 100644 --- a/code/Geometry/GeometryUtils.h +++ b/code/Geometry/GeometryUtils.h @@ -49,6 +49,10 @@ namespace Assimp { // --------------------------------------------------------------------------- class ASSIMP_API GeometryUtils { public: + /// @brief Will calculate the area of a triangle. + /// @param a The first vertex of the triangle. + /// @param b The first vertex of the triangle. + /// @param c The first vertex of the triangle. static ai_real heron( ai_real a, ai_real b, ai_real c ); /// @brief Will compute the distance between 2 3D-vectors diff --git a/code/Material/MaterialSystem.cpp b/code/Material/MaterialSystem.cpp index dbae4b7e4..917f69105 100644 --- a/code/Material/MaterialSystem.cpp +++ b/code/Material/MaterialSystem.cpp @@ -174,6 +174,95 @@ aiReturn aiGetMaterialFloatArray(const aiMaterial *pMat, return AI_SUCCESS; } +// ------------------------------------------------------------------------------------------------ +// Get an array of floating-point values from the material. +aiReturn aiGetMaterialDoubleArray(const aiMaterial *pMat, + const char *pKey, + unsigned int type, + unsigned int index, + double *pOut, + unsigned int *pMax) { + ai_assert(pOut != nullptr); + ai_assert(pMat != nullptr); + + const aiMaterialProperty *prop; + aiGetMaterialProperty(pMat, pKey, type, index, (const aiMaterialProperty **)&prop); + if (nullptr == prop) { + return AI_FAILURE; + } + + // data is given in floats, convert to ai_real + unsigned int iWrite = 0; + if (aiPTI_Float == prop->mType || aiPTI_Buffer == prop->mType) { + iWrite = prop->mDataLength / sizeof(float); + if (pMax) { + iWrite = std::min(*pMax, iWrite); + ; + } + + for (unsigned int a = 0; a < iWrite; ++a) { + pOut[a] = static_cast(reinterpret_cast(prop->mData)[a]); + } + + if (pMax) { + *pMax = iWrite; + } + } + // data is given in doubles, convert to float + else if (aiPTI_Double == prop->mType) { + iWrite = prop->mDataLength / sizeof(double); + if (pMax) { + iWrite = std::min(*pMax, iWrite); + ; + } + for (unsigned int a = 0; a < iWrite; ++a) { + pOut[a] = static_cast(reinterpret_cast(prop->mData)[a]); + } + if (pMax) { + *pMax = iWrite; + } + } + // data is given in ints, convert to float + else if (aiPTI_Integer == prop->mType) { + iWrite = prop->mDataLength / sizeof(int32_t); + if (pMax) { + iWrite = std::min(*pMax, iWrite); + } + for (unsigned int a = 0; a < iWrite; ++a) { + pOut[a] = static_cast(reinterpret_cast(prop->mData)[a]); + } + if (pMax) { + *pMax = iWrite; + } + } + // a string ... read floats separated by spaces + else { + if (pMax) { + iWrite = *pMax; + } + // strings are zero-terminated with a 32 bit length prefix, so this is safe + const char *cur = prop->mData + 4; + ai_assert(prop->mDataLength >= 5); + ai_assert(!prop->mData[prop->mDataLength - 1]); + for (unsigned int a = 0;; ++a) { + cur = fast_atoreal_move(cur, pOut[a]); + if (a == iWrite - 1) { + break; + } + if (!IsSpace(*cur)) { + ASSIMP_LOG_ERROR("Material property", pKey, + " is a string; failed to parse a float array out of it."); + return AI_FAILURE; + } + } + + if (pMax) { + *pMax = iWrite; + } + } + return AI_SUCCESS; +} + // ------------------------------------------------------------------------------------------------ // Get an array if integers from the material aiReturn aiGetMaterialIntegerArray(const aiMaterial *pMat, @@ -486,7 +575,7 @@ aiReturn aiMaterial::AddBinaryProperty(const void *pInput, memcpy(pcNew->mData, pInput, pSizeInBytes); pcNew->mKey.length = static_cast(::strlen(pKey)); - ai_assert(MAXLEN > pcNew->mKey.length); + ai_assert(AI_MAXLEN > pcNew->mKey.length); strcpy(pcNew->mKey.data, pKey); if (UINT_MAX != iOutIndex) { diff --git a/code/PostProcessing/CalcTangentsProcess.cpp b/code/PostProcessing/CalcTangentsProcess.cpp index 82fde1348..8fd063260 100644 --- a/code/PostProcessing/CalcTangentsProcess.cpp +++ b/code/PostProcessing/CalcTangentsProcess.cpp @@ -185,9 +185,9 @@ bool CalcTangentsProcess::ProcessMesh(aiMesh *pMesh, unsigned int meshIndex) { tangent.x = (w.x * sy - v.x * ty) * dirCorrection; tangent.y = (w.y * sy - v.y * ty) * dirCorrection; tangent.z = (w.z * sy - v.z * ty) * dirCorrection; - bitangent.x = (- w.x * sx + v.x * tx) * dirCorrection; - bitangent.y = (- w.y * sx + v.y * tx) * dirCorrection; - bitangent.z = (- w.z * sx + v.z * tx) * dirCorrection; + bitangent.x = (w.x * sx - v.x * tx) * dirCorrection; + bitangent.y = (w.y * sx - v.y * tx) * dirCorrection; + bitangent.z = (w.z * sx - v.z * tx) * dirCorrection; // store for every vertex of that face for (unsigned int b = 0; b < face.mNumIndices; ++b) { @@ -195,13 +195,15 @@ bool CalcTangentsProcess::ProcessMesh(aiMesh *pMesh, unsigned int meshIndex) { // project tangent and bitangent into the plane formed by the vertex' normal aiVector3D localTangent = tangent - meshNorm[p] * (tangent * meshNorm[p]); - aiVector3D localBitangent = bitangent - meshNorm[p] * (bitangent * meshNorm[p]) - localTangent * (bitangent * localTangent); + aiVector3D localBitangent = bitangent - meshNorm[p] * (bitangent * meshNorm[p]); localTangent.NormalizeSafe(); localBitangent.NormalizeSafe(); // reconstruct tangent/bitangent according to normal and bitangent/tangent when it's infinite or NaN. - bool invalid_tangent = is_special_float(localTangent.x) || is_special_float(localTangent.y) || is_special_float(localTangent.z); - bool invalid_bitangent = is_special_float(localBitangent.x) || is_special_float(localBitangent.y) || is_special_float(localBitangent.z); + bool invalid_tangent = is_special_float(localTangent.x) || is_special_float(localTangent.y) || is_special_float(localTangent.z) + || (-0.5f < localTangent.x && localTangent.x < 0.5f && -0.5f < localTangent.y && localTangent.y < 0.5f && -0.5f < localTangent.z && localTangent.z < 0.5f); + bool invalid_bitangent = is_special_float(localBitangent.x) || is_special_float(localBitangent.y) || is_special_float(localBitangent.z) + || (-0.5f < localBitangent.x && localBitangent.x < 0.5f && -0.5f < localBitangent.y && localBitangent.y < 0.5f && -0.5f < localBitangent.z && localBitangent.z < 0.5f); if (invalid_tangent != invalid_bitangent) { if (invalid_tangent) { localTangent = meshNorm[p] ^ localBitangent; diff --git a/code/PostProcessing/ComputeUVMappingProcess.cpp b/code/PostProcessing/ComputeUVMappingProcess.cpp index 2aa34de28..cac51e8d0 100644 --- a/code/PostProcessing/ComputeUVMappingProcess.cpp +++ b/code/PostProcessing/ComputeUVMappingProcess.cpp @@ -346,16 +346,20 @@ void ComputeUVMappingProcess::Execute(aiScene *pScene) { ASSIMP_LOG_DEBUG("GenUVCoordsProcess begin"); char buffer[1024]; - if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) + if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) { throw DeadlyImportError("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here"); + } std::list mappingStack; - /* Iterate through all materials and search for non-UV mapped textures - */ + // Iterate through all materials and search for non-UV mapped textures for (unsigned int i = 0; i < pScene->mNumMaterials; ++i) { mappingStack.clear(); aiMaterial *mat = pScene->mMaterials[i]; + if (mat == nullptr) { + ASSIMP_LOG_INFO("Material pointer in nullptr, skipping."); + continue; + } for (unsigned int a = 0; a < mat->mNumProperties; ++a) { aiMaterialProperty *prop = mat->mProperties[a]; if (!::strcmp(prop->mKey.data, "$tex.mapping")) { diff --git a/code/PostProcessing/FindDegenerates.cpp b/code/PostProcessing/FindDegenerates.cpp index c506b08b5..7401ea0e7 100644 --- a/code/PostProcessing/FindDegenerates.cpp +++ b/code/PostProcessing/FindDegenerates.cpp @@ -143,9 +143,13 @@ bool FindDegeneratesProcess::ExecuteOnMesh(aiMesh *mesh) { for (unsigned int a = 0; a < mesh->mNumFaces; ++a) { aiFace &face = mesh->mFaces[a]; bool first = true; + auto vertex_in_range = [numVertices = mesh->mNumVertices](unsigned int vertex_idx) { return vertex_idx < numVertices; }; // check whether the face contains degenerated entries for (unsigned int i = 0; i < face.mNumIndices; ++i) { + if (!std::all_of(face.mIndices, face.mIndices + face.mNumIndices, vertex_in_range)) + continue; + // Polygons with more than 4 points are allowed to have double points, that is // simulating polygons with holes just with concave polygons. However, // double points may not come directly after another. diff --git a/code/PostProcessing/LimitBoneWeightsProcess.cpp b/code/PostProcessing/LimitBoneWeightsProcess.cpp index 816914ada..71b6f9ec6 100644 --- a/code/PostProcessing/LimitBoneWeightsProcess.cpp +++ b/code/PostProcessing/LimitBoneWeightsProcess.cpp @@ -53,7 +53,8 @@ namespace Assimp { // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer -LimitBoneWeightsProcess::LimitBoneWeightsProcess() : mMaxWeights(AI_LMW_MAX_WEIGHTS) { +LimitBoneWeightsProcess::LimitBoneWeightsProcess() : + mMaxWeights(AI_LMW_MAX_WEIGHTS), mRemoveEmptyBones(true) { // empty } diff --git a/code/PostProcessing/OptimizeGraph.cpp b/code/PostProcessing/OptimizeGraph.cpp index 3be5ff514..01f6fca14 100644 --- a/code/PostProcessing/OptimizeGraph.cpp +++ b/code/PostProcessing/OptimizeGraph.cpp @@ -164,7 +164,7 @@ void OptimizeGraphProcess::CollectNewChildren(aiNode *nd, std::list &n ++it; } if (join_master && !join.empty()) { - join_master->mName.length = ::ai_snprintf(join_master->mName.data, MAXLEN, "$MergedNode_%u", count_merged++); + join_master->mName.length = ::ai_snprintf(join_master->mName.data, AI_MAXLEN, "$MergedNode_%u", count_merged++); unsigned int out_meshes = 0; for (std::list::const_iterator it = join.cbegin(); it != join.cend(); ++it) { diff --git a/code/PostProcessing/PretransformVertices.cpp b/code/PostProcessing/PretransformVertices.cpp index 37727c968..0203ac211 100644 --- a/code/PostProcessing/PretransformVertices.cpp +++ b/code/PostProcessing/PretransformVertices.cpp @@ -635,7 +635,7 @@ void PretransformVertices::Execute(aiScene *pScene) { aiNode *pcNode = new aiNode(); *nodes = pcNode; pcNode->mParent = pScene->mRootNode; - pcNode->mName.length = ai_snprintf(pcNode->mName.data, MAXLEN, "light_%u", i); + pcNode->mName.length = ai_snprintf(pcNode->mName.data, AI_MAXLEN, "light_%u", i); pScene->mLights[i]->mName = pcNode->mName; } // generate camera nodes @@ -643,7 +643,7 @@ void PretransformVertices::Execute(aiScene *pScene) { aiNode *pcNode = new aiNode(); *nodes = pcNode; pcNode->mParent = pScene->mRootNode; - pcNode->mName.length = ::ai_snprintf(pcNode->mName.data, MAXLEN, "cam_%u", i); + pcNode->mName.length = ::ai_snprintf(pcNode->mName.data, AI_MAXLEN, "cam_%u", i); pScene->mCameras[i]->mName = pcNode->mName; } } diff --git a/code/PostProcessing/RemoveRedundantMaterials.cpp b/code/PostProcessing/RemoveRedundantMaterials.cpp index 828fcd0ac..111c233b1 100644 --- a/code/PostProcessing/RemoveRedundantMaterials.cpp +++ b/code/PostProcessing/RemoveRedundantMaterials.cpp @@ -176,7 +176,7 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene) { if (ppcMaterials[idx]) { aiString sz; if( ppcMaterials[idx]->Get(AI_MATKEY_NAME, sz) != AI_SUCCESS ) { - sz.length = ::ai_snprintf(sz.data,MAXLEN,"JoinedMaterial_#%u",p); + sz.length = ::ai_snprintf(sz.data, AI_MAXLEN,"JoinedMaterial_#%u",p); ((aiMaterial*)ppcMaterials[idx])->AddProperty(&sz,AI_MATKEY_NAME); } } else { diff --git a/code/PostProcessing/SplitByBoneCountProcess.cpp b/code/PostProcessing/SplitByBoneCountProcess.cpp index ea50f8f7f..f63478767 100644 --- a/code/PostProcessing/SplitByBoneCountProcess.cpp +++ b/code/PostProcessing/SplitByBoneCountProcess.cpp @@ -394,7 +394,7 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vectormNumMeshes == 0 ) { + if( pNode->mNumMeshes != 0 ) { IndexArray newMeshList; for( unsigned int a = 0; a < pNode->mNumMeshes; ++a) { unsigned int srcIndex = pNode->mMeshes[a]; diff --git a/code/PostProcessing/TextureTransform.cpp b/code/PostProcessing/TextureTransform.cpp index 53dba3860..228e97e42 100644 --- a/code/PostProcessing/TextureTransform.cpp +++ b/code/PostProcessing/TextureTransform.cpp @@ -390,8 +390,8 @@ void TextureTransformStep::Execute( aiScene* pScene) { cnt = 0; for (it = trafo.begin();it != trafo.end(); ++it,++cnt) { if ((*it).lockedPos != AI_TT_UV_IDX_LOCK_NONE && (*it).lockedPos != cnt) { - it2 = trafo.begin();unsigned int t = 0; - while (t != (*it).lockedPos) + it2 = trafo.begin(); + while ((*it2).lockedPos != (*it).lockedPos) ++it2; if ((*it2).lockedPos != AI_TT_UV_IDX_LOCK_NONE) { diff --git a/code/PostProcessing/TriangulateProcess.cpp b/code/PostProcessing/TriangulateProcess.cpp index ac483f215..c0ffffd6b 100644 --- a/code/PostProcessing/TriangulateProcess.cpp +++ b/code/PostProcessing/TriangulateProcess.cpp @@ -192,13 +192,13 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh) { for( unsigned int a = 0; a < pMesh->mNumFaces; a++) { const aiFace& face = pMesh->mFaces[a]; - if( face.mNumIndices != 3) { bNeed = true; } } - if (!bNeed) + if (!bNeed) { return false; + } } else if (!(pMesh->mPrimitiveTypes & aiPrimitiveType_POLYGON)) { return false; @@ -213,8 +213,7 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh) { get_normals = false; } if( face.mNumIndices <= 3) { - numOut++; - + ++numOut; } else { numOut += face.mNumIndices-2; max_out = std::max(max_out,face.mNumIndices); @@ -222,8 +221,11 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh) { } // Just another check whether aiMesh::mPrimitiveTypes is correct - ai_assert(numOut != pMesh->mNumFaces); - + if (numOut == pMesh->mNumFaces) { + ASSIMP_LOG_ERROR( "Invalidation detected in the number of indices: does not fit to the primitive type." ); + return false; + } + aiVector3D *nor_out = nullptr; // if we don't have normals yet, but expect them to be a cheap side diff --git a/code/PostProcessing/ValidateDataStructure.cpp b/code/PostProcessing/ValidateDataStructure.cpp index a0a73e200..8441b48be 100644 --- a/code/PostProcessing/ValidateDataStructure.cpp +++ b/code/PostProcessing/ValidateDataStructure.cpp @@ -371,20 +371,7 @@ void ValidateDSProcess::Validate(const aiMesh *pMesh) { ReportWarning("There are unreferenced vertices"); } - // texture channel 2 may not be set if channel 1 is zero ... - { - unsigned int i = 0; - for (; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { - if (!pMesh->HasTextureCoords(i)) break; - } - for (; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) - if (pMesh->HasTextureCoords(i)) { - ReportError("Texture coordinate channel %i exists " - "although the previous channel was nullptr.", - i); - } - } - // the same for the vertex colors + // vertex color channel 2 may not be set if channel 1 is zero ... { unsigned int i = 0; for (; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) { @@ -909,9 +896,9 @@ void ValidateDSProcess::Validate(const aiNode *pNode) { // ------------------------------------------------------------------------------------------------ void ValidateDSProcess::Validate(const aiString *pString) { - if (pString->length > MAXLEN) { + if (pString->length > AI_MAXLEN) { ReportError("aiString::length is too large (%u, maximum is %lu)", - pString->length, MAXLEN); + pString->length, AI_MAXLEN); } const char *sz = pString->data; while (true) { @@ -920,7 +907,7 @@ void ValidateDSProcess::Validate(const aiString *pString) { ReportError("aiString::data is invalid: the terminal zero is at a wrong offset"); } break; - } else if (sz >= &pString->data[MAXLEN]) { + } else if (sz >= &pString->data[AI_MAXLEN]) { ReportError("aiString::data is invalid. There is no terminal character"); } ++sz; diff --git a/code/res/assimp.rc b/code/res/assimp.rc index fd10935f6..6c9356276 100644 --- a/code/res/assimp.rc +++ b/code/res/assimp.rc @@ -1,4 +1,4 @@ -#include "revision.h" +#include #ifdef __GNUC__ #include "winresrc.h" #else diff --git a/contrib/draco/BUILDING.md b/contrib/draco/BUILDING.md index 340b2b83b..8e1f13e1f 100644 --- a/contrib/draco/BUILDING.md +++ b/contrib/draco/BUILDING.md @@ -1,21 +1,23 @@ _**Contents**_ - * [CMake Basics](#cmake-basics) - * [Mac OS X](#mac-os-x) - * [Windows](#windows) - * [CMake Build Configuration](#cmake-build-configuration) - * [Transcoder](#transcoder) - * [Debugging and Optimization](#debugging-and-optimization) - * [Googletest Integration](#googletest-integration) - * [Third Party Libraries](#third-party-libraries) - * [Javascript Encoder/Decoder](#javascript-encoderdecoder) - * [WebAssembly Decoder](#webassembly-decoder) - * [WebAssembly Mesh Only Decoder](#webassembly-mesh-only-decoder) - * [WebAssembly Point Cloud Only Decoder](#webassembly-point-cloud-only-decoder) - * [iOS Builds](#ios-builds) - * [Android Studio Project Integration](#android-studio-project-integration) - * [Native Android Builds](#native-android-builds) - * [vcpkg](#vcpkg) +- [Building](#building) + - [CMake Basics](#cmake-basics) + - [Mac OS X](#mac-os-x) + - [Windows](#windows) + - [CMake Build Configuration](#cmake-build-configuration) + - [Transcoder](#transcoder) + - [Debugging and Optimization](#debugging-and-optimization) + - [Googletest Integration](#googletest-integration) + - [Third Party Libraries](#third-party-libraries) + - [WebAssembly Decoder](#webassembly-decoder) + - [WebAssembly Mesh Only Decoder](#webassembly-mesh-only-decoder) + - [WebAssembly Point Cloud Only Decoder](#webassembly-point-cloud-only-decoder) + - [Javascript Encoder/Decoder](#javascript-encoderdecoder) + - [iOS Builds](#ios-builds) + - [Native Android Builds](#native-android-builds) + - [Android Studio Project Integration](#android-studio-project-integration) + - [Draco - Static Library](#draco---static-library) + - [vcpkg](#vcpkg) Building ======== @@ -325,7 +327,7 @@ Draco - Static Library To include Draco in an existing or new Android Studio project, reference it from the `cmake` file of an existing native project that has a minimum SDK -version of 18 or higher. The project must support C++11. +version of 18 or higher. The project must support C++11 at least. To add Draco to your project: 1. Create a new "Native C++" project. diff --git a/contrib/poly2tri/LICENSE b/contrib/poly2tri/LICENSE index 9417c0836..dddc3ccc3 100644 --- a/contrib/poly2tri/LICENSE +++ b/contrib/poly2tri/LICENSE @@ -1,7 +1,6 @@ -Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors -http://code.google.com/p/poly2tri/ - +Copyright (c) 2009-2018, Poly2Tri Contributors All rights reserved. + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/contrib/poly2tri/README b/contrib/poly2tri/README deleted file mode 100644 index 883e9a581..000000000 --- a/contrib/poly2tri/README +++ /dev/null @@ -1,51 +0,0 @@ -================== -INSTALLATION GUIDE -================== - ------------- -Dependencies ------------- - - Core poly2tri lib: - - Standard Template Library (STL) - - Testbed: - - gcc - - OpenGL - - GLFW (http://glfw.sf.net) - - Python - -Waf (http://code.google.com/p/waf/) is used to compile the testbed. -A waf script (86kb) is included in the repositoty. - ----------------------------------------------- -Building the Testbed ----------------------------------------------- - -Posix/MSYS environment: - - ./waf configure - ./waf build - -Windows command line: - - python waf configure - python waf build - ----------------------------------------------- -Running the Examples ----------------------------------------------- - -Load data points from a file: -p2t - -Random distribution of points inside a consrained box: -p2t random - -Examples: - - ./p2t dude.dat 300 500 2 - ./p2t nazca_monkey.dat 0 0 9 - - ./p2t random 10 100 5.0 - ./p2t random 1000 20000 0.025 diff --git a/contrib/poly2tri/README.md b/contrib/poly2tri/README.md new file mode 100644 index 000000000..88a0618ea --- /dev/null +++ b/contrib/poly2tri/README.md @@ -0,0 +1,101 @@ +Since there are no Input validation of the data given for triangulation you need +to think about this. Poly2Tri does not support repeat points within epsilon. + +* If you have a cyclic function that generates random points make sure you don't + add the same coordinate twice. +* If you are given input and aren't sure same point exist twice you need to + check for this yourself. +* Only simple polygons are supported. You may add holes or interior Steiner points +* Interior holes must not touch other holes, nor touch the polyline boundary +* Use the library in this order: + 1. Initialize CDT with a simple polyline (this defines the constrained edges) + 2. Add holes if necessary (also simple polylines) + 3. Add Steiner points + 4. Triangulate + +Make sure you understand the preceding notice before posting an issue. If you have +an issue not covered by the above, include your data-set with the problem. +The only easy day was yesterday; have a nice day. + +TESTBED INSTALLATION GUIDE +========================== + +Dependencies +------------ + +Core poly2tri lib: + +* Standard Template Library (STL) + +Unit tests: +* Boost (filesystem, test framework) + +Testbed: + +* OpenGL +* [GLFW](http://glfw.sf.net) + +Build the library +----------------- + +With the ninja build system installed: + +``` +mkdir build && cd build +cmake -GNinja .. +cmake --build . +``` + +Build and run with unit tests +---------------------------- + +With the ninja build system: + +``` +mkdir build && cd build +cmake -GNinja -DP2T_BUILD_TESTS=ON .. +cmake --build . +ctest --output-on-failure +``` + +Build with the testbed +----------------- + +``` +mkdir build && cd build +cmake -GNinja -DP2T_BUILD_TESTBED=ON .. +cmake --build . +``` + +Running the Examples +-------------------- + +Load data points from a file: +``` +build/testbed/p2t +``` +Load data points from a file and automatically fit the geometry to the window: +``` +build/testbed/p2t +``` +Random distribution of points inside a constrained box: +``` +build/testbed/p2t random +``` +Examples: +``` +build/testbed/p2t testbed/data/dude.dat 350 500 3 + +build/testbed/p2t testbed/data/nazca_monkey.dat + +build/testbed/p2t random 10 100 5.0 +build/testbed/p2t random 1000 20000 0.025 +``` + +References +========== + +- Domiter V. and Zalik B. (2008) Sweep‐line algorithm for constrained Delaunay triangulation +- FlipScan by library author Thomas Åhlén + +![FlipScan](doc/FlipScan.png) diff --git a/contrib/poly2tri/poly2tri/common/dll_symbol.h b/contrib/poly2tri/poly2tri/common/dll_symbol.h new file mode 100644 index 000000000..c18c2baec --- /dev/null +++ b/contrib/poly2tri/poly2tri/common/dll_symbol.h @@ -0,0 +1,61 @@ +/* + * Poly2Tri Copyright (c) 2009-2022, Poly2Tri Contributors + * https://github.com/jhasse/poly2tri + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if defined(_WIN32) +# pragma warning( disable: 4273) +# define P2T_COMPILER_DLLEXPORT __declspec(dllexport) +# define P2T_COMPILER_DLLIMPORT __declspec(dllimport) +#elif defined(__GNUC__) +# define P2T_COMPILER_DLLEXPORT __attribute__ ((visibility ("default"))) +# define P2T_COMPILER_DLLIMPORT __attribute__ ((visibility ("default"))) +#else +# define P2T_COMPILER_DLLEXPORT +# define P2T_COMPILER_DLLIMPORT +#endif + +// We need to enable shard linkage explicitely +#ifdef ASSIMP_BUILD_DLL_EXPORT +# define P2T_SHARED_EXPORTS 1 +#endif + +#ifndef P2T_DLL_SYMBOL +# if defined(P2T_STATIC_EXPORTS) +# define P2T_DLL_SYMBOL +# elif defined(P2T_SHARED_EXPORTS) +# define P2T_DLL_SYMBOL P2T_COMPILER_DLLEXPORT +# elif defined(BUILD_SHARED_LIBS) +# define P2T_DLL_SYMBOL P2T_COMPILER_DLLIMPORT +# else +# define P2T_DLL_SYMBOL +# endif +#endif diff --git a/contrib/poly2tri/poly2tri/common/shapes.cc b/contrib/poly2tri/poly2tri/common/shapes.cc index c94e11c03..359ef7586 100644 --- a/contrib/poly2tri/poly2tri/common/shapes.cc +++ b/contrib/poly2tri/poly2tri/common/shapes.cc @@ -1,6 +1,6 @@ /* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ + * Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors + * https://github.com/jhasse/poly2tri * * All rights reserved. * @@ -29,14 +29,24 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "shapes.h" + +#include #include namespace p2t { +Point::Point(double x, double y) : x(x), y(y) +{ +} + +std::ostream& operator<<(std::ostream& out, const Point& point) { + return out << point.x << "," << point.y; +} + Triangle::Triangle(Point& a, Point& b, Point& c) { points_[0] = &a; points_[1] = &b; points_[2] = &c; - neighbors_[0] = NULL; neighbors_[1] = NULL; neighbors_[2] = NULL; + neighbors_[0] = nullptr; neighbors_[1] = nullptr; neighbors_[2] = nullptr; constrained_edge[0] = constrained_edge[1] = constrained_edge[2] = false; delaunay_edge[0] = delaunay_edge[1] = delaunay_edge[2] = false; interior_ = false; @@ -76,39 +86,37 @@ void Triangle::MarkNeighbor(Triangle& t) void Triangle::Clear() { Triangle *t; - for( int i=0; i<3; i++ ) - { - t = neighbors_[i]; - if( t != NULL ) - { - t->ClearNeighbor( this ); - } + for (auto& neighbor : neighbors_) { + t = neighbor; + if (t != nullptr) { + t->ClearNeighbor(this); + } } ClearNeighbors(); - points_[0]=points_[1]=points_[2] = NULL; + points_[0]=points_[1]=points_[2] = nullptr; } void Triangle::ClearNeighbor(const Triangle *triangle ) { if( neighbors_[0] == triangle ) { - neighbors_[0] = NULL; + neighbors_[0] = nullptr; } else if( neighbors_[1] == triangle ) { - neighbors_[1] = NULL; + neighbors_[1] = nullptr; } else { - neighbors_[2] = NULL; + neighbors_[2] = nullptr; } } void Triangle::ClearNeighbors() { - neighbors_[0] = NULL; - neighbors_[1] = NULL; - neighbors_[2] = NULL; + neighbors_[0] = nullptr; + neighbors_[1] = nullptr; + neighbors_[2] = nullptr; } void Triangle::ClearDelunayEdges() @@ -220,7 +228,7 @@ Point* Triangle::PointCW(const Point& point) return points_[1]; } assert(0); - return NULL; + return nullptr; } // The point counter-clockwise to given point @@ -234,7 +242,18 @@ Point* Triangle::PointCCW(const Point& point) return points_[0]; } assert(0); - return NULL; + return nullptr; +} + +// The neighbor across to given point +Triangle* Triangle::NeighborAcross(const Point& point) +{ + if (&point == points_[0]) { + return neighbors_[0]; + } else if (&point == points_[1]) { + return neighbors_[1]; + } + return neighbors_[2]; } // The neighbor clockwise to given point @@ -343,23 +362,50 @@ void Triangle::SetDelunayEdgeCW(const Point& p, bool e) } } -// The neighbor across to given point -Triangle& Triangle::NeighborAcross(const Point& opoint) -{ - if (&opoint == points_[0]) { - return *neighbors_[0]; - } else if (&opoint == points_[1]) { - return *neighbors_[1]; - } - return *neighbors_[2]; -} - void Triangle::DebugPrint() { - using namespace std; - cout << points_[0]->x << "," << points_[0]->y << " "; - cout << points_[1]->x << "," << points_[1]->y << " "; - cout << points_[2]->x << "," << points_[2]->y << endl; + std::cout << *points_[0] << " " << *points_[1] << " " << *points_[2] << std::endl; } +bool Triangle::CircumcicleContains(const Point& point) const +{ + assert(IsCounterClockwise()); + const double dx = points_[0]->x - point.x; + const double dy = points_[0]->y - point.y; + const double ex = points_[1]->x - point.x; + const double ey = points_[1]->y - point.y; + const double fx = points_[2]->x - point.x; + const double fy = points_[2]->y - point.y; + + const double ap = dx * dx + dy * dy; + const double bp = ex * ex + ey * ey; + const double cp = fx * fx + fy * fy; + + return (dx * (fy * bp - cp * ey) - dy * (fx * bp - cp * ex) + ap * (fx * ey - fy * ex)) < 0; } + +bool Triangle::IsCounterClockwise() const +{ + return (points_[1]->x - points_[0]->x) * (points_[2]->y - points_[0]->y) - + (points_[2]->x - points_[0]->x) * (points_[1]->y - points_[0]->y) > + 0; +} + +bool IsDelaunay(const std::vector& triangles) +{ + for (const auto triangle : triangles) { + for (const auto other : triangles) { + if (triangle == other) { + continue; + } + for (int i = 0; i < 3; ++i) { + if (triangle->CircumcicleContains(*other->GetPoint(i))) { + return false; + } + } + } + } + return true; +} + +} // namespace p2t diff --git a/contrib/poly2tri/poly2tri/common/shapes.h b/contrib/poly2tri/poly2tri/common/shapes.h index d3660f716..3f4d1c02f 100644 --- a/contrib/poly2tri/poly2tri/common/shapes.h +++ b/contrib/poly2tri/poly2tri/common/shapes.h @@ -1,6 +1,6 @@ /* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ + * Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors + * https://github.com/jhasse/poly2tri * * All rights reserved. * @@ -29,22 +29,24 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -// Include guard -#ifndef SHAPES_H -#define SHAPES_H +#pragma once -#include +#include "dll_symbol.h" + +#include #include #include -#include -#include -#include +#include + +#if defined(_WIN32) +# pragma warning( disable: 4251) +#endif namespace p2t { struct Edge; -struct Point { +struct P2T_DLL_SYMBOL Point { double x, y; @@ -59,7 +61,7 @@ struct Point { std::vector edge_list; /// Construct using coordinates. - Point(double x, double y) : x(x), y(y) {} + Point(double x, double y); /// Set this point to all zeros. void set_zero() @@ -121,8 +123,10 @@ struct Point { }; +P2T_DLL_SYMBOL std::ostream& operator<<(std::ostream&, const Point&); + // Represents a simple polygon's edge -struct Edge { +struct P2T_DLL_SYMBOL Edge { Point* p, *q; @@ -138,9 +142,7 @@ struct Edge { p = &p2; } else if (p1.x == p2.x) { // Repeat points - // ASSIMP_CHANGE (aramis_acg) - throw std::runtime_error(std::string("repeat points")); - //assert(false); + throw std::runtime_error("Edge::Edge: p1 == p2"); } } @@ -151,7 +153,7 @@ struct Edge { // Triangle-based data structures are know to have better performance than quad-edge structures // See: J. Shewchuk, "Triangle: Engineering a 2D Quality Mesh Generator and Delaunay Triangulator" // "Triangulations in CGAL" -class Triangle { +class P2T_DLL_SYMBOL Triangle { public: /// Constructor @@ -178,6 +180,7 @@ void MarkConstrainedEdge(Point* p, Point* q); int Index(const Point* p); int EdgeIndex(const Point* p1, const Point* p2); +Triangle* NeighborAcross(const Point& point); Triangle* NeighborCW(const Point& point); Triangle* NeighborCCW(const Point& point); bool GetConstrainedEdgeCCW(const Point& p); @@ -205,12 +208,14 @@ void ClearDelunayEdges(); inline bool IsInterior(); inline void IsInterior(bool b); -Triangle& NeighborAcross(const Point& opoint); - void DebugPrint(); +bool CircumcicleContains(const Point&) const; + private: +bool IsCounterClockwise() const; + /// Triangle points Point* points_[3]; /// Neighbor list @@ -258,7 +263,7 @@ inline bool operator ==(const Point& a, const Point& b) inline bool operator !=(const Point& a, const Point& b) { - return !(a.x == b.x) && !(a.y == b.y); + return !(a.x == b.x) || !(a.y == b.y); } /// Peform the dot product on two vectors. @@ -322,6 +327,7 @@ inline void Triangle::IsInterior(bool b) interior_ = b; } -} +/// Is this set a valid delaunay triangulation? +P2T_DLL_SYMBOL bool IsDelaunay(const std::vector&); -#endif +} diff --git a/contrib/poly2tri/poly2tri/common/utils.h b/contrib/poly2tri/poly2tri/common/utils.h index 2424c712c..c9f1c23c3 100644 --- a/contrib/poly2tri/poly2tri/common/utils.h +++ b/contrib/poly2tri/poly2tri/common/utils.h @@ -1,6 +1,6 @@ /* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ + * Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors + * https://github.com/jhasse/poly2tri * * All rights reserved. * @@ -29,14 +29,15 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef UTILS_H -#define UTILS_H +#pragma once // Otherwise #defines like M_PI are undeclared under Visual Studio #define _USE_MATH_DEFINES +#include "shapes.h" + +#include #include -#include // C99 removes M_PI from math.h #ifndef M_PI @@ -66,7 +67,11 @@ Orientation Orient2d(const Point& pa, const Point& pb, const Point& pc) double detleft = (pa.x - pc.x) * (pb.y - pc.y); double detright = (pa.y - pc.y) * (pb.x - pc.x); double val = detleft - detright; - if (val > -EPSILON && val < EPSILON) { + +// Using a tolerance here fails on concave-by-subepsilon boundaries +// if (val > -EPSILON && val < EPSILON) { +// Using == on double makes -Wfloat-equal warnings yell at us + if (std::fpclassify(val) == FP_ZERO) { return COLLINEAR; } else if (val > 0) { return CCW; @@ -123,5 +128,3 @@ bool InScanArea(const Point& pa, const Point& pb, const Point& pc, const Point& } } - -#endif diff --git a/contrib/poly2tri/poly2tri/poly2tri.h b/contrib/poly2tri/poly2tri/poly2tri.h index ba5cc159e..3d40373b0 100644 --- a/contrib/poly2tri/poly2tri/poly2tri.h +++ b/contrib/poly2tri/poly2tri/poly2tri.h @@ -1,6 +1,6 @@ /* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ + * Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors + * https://github.com/jhasse/poly2tri * * All rights reserved. * @@ -29,10 +29,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef POLY2TRI_H -#define POLY2TRI_H +#pragma once #include "common/shapes.h" #include "sweep/cdt.h" - -#endif diff --git a/contrib/poly2tri/poly2tri/sweep/advancing_front.cc b/contrib/poly2tri/poly2tri/sweep/advancing_front.cc index 9739babce..e981bad2f 100644 --- a/contrib/poly2tri/poly2tri/sweep/advancing_front.cc +++ b/contrib/poly2tri/poly2tri/sweep/advancing_front.cc @@ -1,6 +1,6 @@ /* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ + * Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors + * https://github.com/jhasse/poly2tri * * All rights reserved. * @@ -30,6 +30,8 @@ */ #include "advancing_front.h" +#include + namespace p2t { AdvancingFront::AdvancingFront(Node& head, Node& tail) @@ -44,21 +46,21 @@ Node* AdvancingFront::LocateNode(double x) Node* node = search_node_; if (x < node->value) { - while ((node = node->prev) != NULL) { + while ((node = node->prev) != nullptr) { if (x >= node->value) { search_node_ = node; return node; } } } else { - while ((node = node->next) != NULL) { + while ((node = node->next) != nullptr) { if (x < node->value) { search_node_ = node->prev; return node->prev; } } } - return NULL; + return nullptr; } Node* AdvancingFront::FindSearchNode(double x) @@ -86,13 +88,13 @@ Node* AdvancingFront::LocatePoint(const Point* point) } } } else if (px < nx) { - while ((node = node->prev) != NULL) { + while ((node = node->prev) != nullptr) { if (point == node->point) { break; } } } else { - while ((node = node->next) != NULL) { + while ((node = node->next) != nullptr) { if (point == node->point) break; } @@ -105,4 +107,4 @@ AdvancingFront::~AdvancingFront() { } -} +} // namespace p2t diff --git a/contrib/poly2tri/poly2tri/sweep/advancing_front.h b/contrib/poly2tri/poly2tri/sweep/advancing_front.h index 3bfec5368..ffd3fe71b 100644 --- a/contrib/poly2tri/poly2tri/sweep/advancing_front.h +++ b/contrib/poly2tri/poly2tri/sweep/advancing_front.h @@ -1,6 +1,6 @@ /* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ + * Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors + * https://github.com/jhasse/poly2tri * * All rights reserved. * @@ -29,8 +29,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ADVANCED_FRONT_H -#define ADVANCED_FRONT_H +#pragma once #include "../common/shapes.h" @@ -114,5 +113,3 @@ inline void AdvancingFront::set_search(Node* node) } } - -#endif diff --git a/contrib/poly2tri/poly2tri/sweep/cdt.cc b/contrib/poly2tri/poly2tri/sweep/cdt.cc index b79f5a8de..4dfe6a641 100644 --- a/contrib/poly2tri/poly2tri/sweep/cdt.cc +++ b/contrib/poly2tri/poly2tri/sweep/cdt.cc @@ -1,6 +1,6 @@ /* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ + * Poly2Tri Copyright (c) 2009-2021, Poly2Tri Contributors + * https://github.com/jhasse/poly2tri * * All rights reserved. * @@ -68,4 +68,4 @@ CDT::~CDT() delete sweep_; } -} +} // namespace p2t diff --git a/contrib/poly2tri/poly2tri/sweep/cdt.h b/contrib/poly2tri/poly2tri/sweep/cdt.h index 4a9a292d3..7c54ea976 100644 --- a/contrib/poly2tri/poly2tri/sweep/cdt.h +++ b/contrib/poly2tri/poly2tri/sweep/cdt.h @@ -1,6 +1,6 @@ /* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ + * Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors + * https://github.com/jhasse/poly2tri * * All rights reserved. * @@ -29,13 +29,14 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef CDT_H -#define CDT_H +#pragma once #include "advancing_front.h" #include "sweep_context.h" #include "sweep.h" +#include "../common/dll_symbol.h" + /** * * @author Mason Green @@ -44,7 +45,7 @@ namespace p2t { -class CDT +class P2T_DLL_SYMBOL CDT { public: @@ -101,5 +102,3 @@ public: }; } - -#endif diff --git a/contrib/poly2tri/poly2tri/sweep/sweep.cc b/contrib/poly2tri/poly2tri/sweep/sweep.cc index e1f23f11b..48e8bee8f 100644 --- a/contrib/poly2tri/poly2tri/sweep/sweep.cc +++ b/contrib/poly2tri/poly2tri/sweep/sweep.cc @@ -1,6 +1,6 @@ /* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ + * Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors + * https://github.com/jhasse/poly2tri * * All rights reserved. * @@ -28,24 +28,21 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include #include "sweep.h" #include "sweep_context.h" #include "advancing_front.h" #include "../common/utils.h" -namespace p2t { +#include +#include -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning( disable : 4702 ) -#endif // _MSC_VER +namespace p2t { // Triangulate simple polygon with holes void Sweep::Triangulate(SweepContext& tcx) { tcx.InitTriangulation(); - tcx.CreateAdvancingFront(nodes_); + tcx.CreateAdvancingFront(); // Sweep points; build mesh SweepPoints(tcx); // Clean up @@ -57,8 +54,8 @@ void Sweep::SweepPoints(SweepContext& tcx) for (size_t i = 1; i < tcx.point_count(); i++) { Point& point = *tcx.GetPoint(i); Node* node = &PointEvent(tcx, point); - for (unsigned int ii = 0; ii < point.edge_list.size(); ii++) { - EdgeEvent(tcx, point.edge_list[ii], node); + for (auto& j : point.edge_list) { + EdgeEvent(tcx, j, node); } } } @@ -68,17 +65,25 @@ void Sweep::FinalizationPolygon(SweepContext& tcx) // Get an Internal triangle to start with Triangle* t = tcx.front()->head()->next->triangle; Point* p = tcx.front()->head()->next->point; - while (!t->GetConstrainedEdgeCW(*p)) { + while (t && !t->GetConstrainedEdgeCW(*p)) { t = t->NeighborCCW(*p); } // Collect interior triangles constrained by edges - tcx.MeshClean(*t); + if (t) { + tcx.MeshClean(*t); + } } Node& Sweep::PointEvent(SweepContext& tcx, Point& point) { - Node& node = tcx.LocateNode(point); + Node* node_ptr = tcx.LocateNode(point); + if (!node_ptr || !node_ptr->point || !node_ptr->next || !node_ptr->next->point) + { + throw std::runtime_error("PointEvent - null node"); + } + + Node& node = *node_ptr; Node& new_node = NewFrontTriangle(tcx, point, node); // Only need to check +epsilon since point never have smaller @@ -111,9 +116,9 @@ void Sweep::EdgeEvent(SweepContext& tcx, Edge* edge, Node* node) void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangle, Point& point) { - if (triangle == nullptr) - return; - + if (triangle == nullptr) { + throw std::runtime_error("EdgeEvent - null triangle"); + } if (IsEdgeSideOfTriangle(*triangle, ep, eq)) { return; } @@ -121,17 +126,14 @@ void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangl Point* p1 = triangle->PointCCW(point); Orientation o1 = Orient2d(eq, *p1, ep); if (o1 == COLLINEAR) { - - - if( triangle->Contains(&eq, p1)) { - triangle->MarkConstrainedEdge(&eq, p1 ); + if (triangle->Contains(&eq, p1)) { + triangle->MarkConstrainedEdge(&eq, p1); // We are modifying the constraint maybe it would be better to // not change the given constraint and just keep a variable for the new constraint tcx.edge_event.constrained_edge->q = p1; - triangle = &triangle->NeighborAcross(point); - EdgeEvent( tcx, ep, *p1, triangle, *p1 ); + triangle = triangle->NeighborAcross(point); + EdgeEvent(tcx, ep, *p1, triangle, *p1); } else { - // ASSIMP_CHANGE (aramis_acg) throw std::runtime_error("EdgeEvent - collinear points not supported"); } return; @@ -140,18 +142,14 @@ void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangl Point* p2 = triangle->PointCW(point); Orientation o2 = Orient2d(eq, *p2, ep); if (o2 == COLLINEAR) { - - - - if( triangle->Contains(&eq, p2)) { - triangle->MarkConstrainedEdge(&eq, p2 ); + if (triangle->Contains(&eq, p2)) { + triangle->MarkConstrainedEdge(&eq, p2); // We are modifying the constraint maybe it would be better to // not change the given constraint and just keep a variable for the new constraint tcx.edge_event.constrained_edge->q = p2; - triangle = &triangle->NeighborAcross(point); - EdgeEvent( tcx, ep, *p2, triangle, *p2 ); + triangle = triangle->NeighborAcross(point); + EdgeEvent(tcx, ep, *p2, triangle, *p2); } else { - // ASSIMP_CHANGE (aramis_acg) throw std::runtime_error("EdgeEvent - collinear points not supported"); } return; @@ -162,12 +160,13 @@ void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangl // that will cross edge if (o1 == CW) { triangle = triangle->NeighborCCW(point); - } else{ + } else { triangle = triangle->NeighborCW(point); } EdgeEvent(tcx, ep, eq, triangle, point); } else { // This triangle crosses constraint so lets flippin start! + assert(triangle); FlipEdgeEvent(tcx, ep, eq, triangle, point); } } @@ -228,7 +227,6 @@ void Sweep::Fill(SweepContext& tcx, Node& node) if (!Legalize(tcx, *triangle)) { tcx.MapTriangleToNodes(*triangle); } - } void Sweep::FillAdvancingFront(SweepContext& tcx, Node& n) @@ -237,7 +235,7 @@ void Sweep::FillAdvancingFront(SweepContext& tcx, Node& n) // Fill right holes Node* node = n.next; - while (node->next) { + while (node && node->next) { // if HoleAngle exceeds 90 degrees then break. if (LargeHole_DontFill(node)) break; Fill(tcx, *node); @@ -247,7 +245,7 @@ void Sweep::FillAdvancingFront(SweepContext& tcx, Node& n) // Fill left holes node = n.prev; - while (node->prev) { + while (node && node->prev) { // if HoleAngle exceeds 90 degrees then break. if (LargeHole_DontFill(node)) break; Fill(tcx, *node); @@ -264,6 +262,35 @@ void Sweep::FillAdvancingFront(SweepContext& tcx, Node& n) } // True if HoleAngle exceeds 90 degrees. +// LargeHole_DontFill checks if the advancing front has a large hole. +// A "Large hole" is a triangle formed by a sequence of points in the advancing +// front where three neighbor points form a triangle. +// And angle between left-top, bottom, and right-top points is more than 90 degrees. +// The first part of the algorithm reviews only three neighbor points, e.g. named A, B, C. +// Additional part of this logic reviews a sequence of 5 points - +// additionally reviews one point before and one after the sequence of three (A, B, C), +// e.g. named X and Y. +// In this case, angles are XBC and ABY and this if angles are negative or more +// than 90 degrees LargeHole_DontFill returns true. +// But there is a configuration when ABC has a negative angle but XBC or ABY is less +// than 90 degrees and positive. +// Then function LargeHole_DontFill return false and initiates filling. +// This filling creates a triangle ABC and adds it to the advancing front. +// But in the case when angle ABC is negative this triangle goes inside the advancing front +// and can intersect previously created triangles. +// This triangle leads to making wrong advancing front and problems in triangulation in the future. +// Looks like such a triangle should not be created. +// The simplest way to check and fix it is to check an angle ABC. +// If it is negative LargeHole_DontFill should return true and +// not initiate creating the ABC triangle in the advancing front. +// X______A Y +// \ / +// \ / +// \ B / +// | / +// | / +// |/ +// C bool Sweep::LargeHole_DontFill(const Node* node) const { const Node* nextNode = node->next; @@ -271,20 +298,28 @@ bool Sweep::LargeHole_DontFill(const Node* node) const { if (!AngleExceeds90Degrees(node->point, nextNode->point, prevNode->point)) return false; + if (AngleIsNegative(node->point, nextNode->point, prevNode->point)) + return true; + // Check additional points on front. const Node* next2Node = nextNode->next; // "..Plus.." because only want angles on same side as point being added. - if ((next2Node != NULL) && !AngleExceedsPlus90DegreesOrIsNegative(node->point, next2Node->point, prevNode->point)) + if ((next2Node != nullptr) && !AngleExceedsPlus90DegreesOrIsNegative(node->point, next2Node->point, prevNode->point)) return false; const Node* prev2Node = prevNode->prev; // "..Plus.." because only want angles on same side as point being added. - if ((prev2Node != NULL) && !AngleExceedsPlus90DegreesOrIsNegative(node->point, nextNode->point, prev2Node->point)) + if ((prev2Node != nullptr) && !AngleExceedsPlus90DegreesOrIsNegative(node->point, nextNode->point, prev2Node->point)) return false; return true; } +bool Sweep::AngleIsNegative(const Point* origin, const Point* pa, const Point* pb) const { + const double angle = Angle(origin, pa, pb); + return angle < 0; +} + bool Sweep::AngleExceeds90Degrees(const Point* origin, const Point* pa, const Point* pb) const { const double angle = Angle(origin, pa, pb); return ((angle > PI_div2) || (angle < -PI_div2)); @@ -306,7 +341,7 @@ double Sweep::Angle(const Point* origin, const Point* pa, const Point* pb) const */ const double px = origin->x; const double py = origin->y; - const double ax = pa->x- px; + const double ax = pa->x - px; const double ay = pa->y - py; const double bx = pb->x - px; const double by = pb->y - py; @@ -599,7 +634,7 @@ void Sweep::FillRightBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) { // Concave FillRightConcaveEdgeEvent(tcx, edge, node); - } else{ + } else { // Convex FillRightConvexEdgeEvent(tcx, edge, node); // Retry this one @@ -623,7 +658,6 @@ void Sweep::FillRightConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) } } } - } void Sweep::FillRightConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) @@ -632,13 +666,13 @@ void Sweep::FillRightConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) if (Orient2d(*node.next->point, *node.next->next->point, *node.next->next->next->point) == CCW) { // Concave FillRightConcaveEdgeEvent(tcx, edge, *node.next); - } else{ + } else { // Convex // Next above or below edge? if (Orient2d(*edge->q, *node.next->next->point, *edge->p) == CCW) { // Below FillRightConvexEdgeEvent(tcx, edge, *node.next); - } else{ + } else { // Above } } @@ -677,13 +711,13 @@ void Sweep::FillLeftConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) if (Orient2d(*node.prev->point, *node.prev->prev->point, *node.prev->prev->prev->point) == CW) { // Concave FillLeftConcaveEdgeEvent(tcx, edge, *node.prev); - } else{ + } else { // Convex // Next above or below edge? if (Orient2d(*edge->q, *node.prev->prev->point, *edge->p) == CW) { // Below FillLeftConvexEdgeEvent(tcx, edge, *node.prev); - } else{ + } else { // Above } } @@ -699,17 +733,22 @@ void Sweep::FillLeftConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) if (Orient2d(*node.point, *node.prev->point, *node.prev->prev->point) == CW) { // Next is concave FillLeftConcaveEdgeEvent(tcx, edge, node); - } else{ + } else { // Next is convex } } } - } void Sweep::FlipEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* t, Point& p) { - Triangle& ot = t->NeighborAcross(p); + assert(t); + Triangle* ot_ptr = t->NeighborAcross(p); + if (ot_ptr == nullptr) + { + throw std::runtime_error("FlipEdgeEvent - null neighbor across"); + } + Triangle& ot = *ot_ptr; Point& op = *ot.OppositePoint(*t, p); if (InScanArea(p, *t->PointCCW(p), *t->PointCW(p), op)) { @@ -775,10 +814,26 @@ Point& Sweep::NextFlipPoint(Point& ep, Point& eq, Triangle& ot, Point& op) void Sweep::FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& flip_triangle, Triangle& t, Point& p) { - Triangle& ot = t.NeighborAcross(p); - Point& op = *ot.OppositePoint(t, p); + Triangle* ot_ptr = t.NeighborAcross(p); + if (ot_ptr == nullptr) { + throw std::runtime_error("FlipScanEdgeEvent - null neighbor across"); + } - if (InScanArea(eq, *flip_triangle.PointCCW(eq), *flip_triangle.PointCW(eq), op)) { + Point* op_ptr = ot_ptr->OppositePoint(t, p); + if (op_ptr == nullptr) { + throw std::runtime_error("FlipScanEdgeEvent - null opposing point"); + } + + Point* p1 = flip_triangle.PointCCW(eq); + Point* p2 = flip_triangle.PointCW(eq); + if (p1 == nullptr || p2 == nullptr) { + throw std::runtime_error("FlipScanEdgeEvent - null on either of points"); + } + + Triangle& ot = *ot_ptr; + Point& op = *op_ptr; + + if (InScanArea(eq, *p1, *p2, op)) { // flip with new edge op->eq FlipEdgeEvent(tcx, eq, op, &ot, op); // TODO: Actually I just figured out that it should be possible to @@ -788,7 +843,7 @@ void Sweep::FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& // also need to set a new flip_triangle first // Turns out at first glance that this is somewhat complicated // so it will have to wait. - } else{ + } else { Point& newP = NextFlipPoint(ep, eq, ot, op); FlipScanEdgeEvent(tcx, ep, eq, flip_triangle, ot, newP); } @@ -797,14 +852,9 @@ void Sweep::FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& Sweep::~Sweep() { // Clean up memory - for(size_t i = 0; i < nodes_.size(); i++) { - delete nodes_[i]; + for (auto& node : nodes_) { + delete node; } - } -#ifdef _MSC_VER -# pragma warning( pop ) -#endif // _MSC_VER - -} +} // namespace p2t diff --git a/contrib/poly2tri/poly2tri/sweep/sweep.h b/contrib/poly2tri/poly2tri/sweep/sweep.h index ad429fd96..ad43f2e4a 100644 --- a/contrib/poly2tri/poly2tri/sweep/sweep.h +++ b/contrib/poly2tri/poly2tri/sweep/sweep.h @@ -1,6 +1,6 @@ /* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ + * Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors + * https://github.com/jhasse/poly2tri * * All rights reserved. * @@ -33,11 +33,10 @@ * Zalik, B.(2008)'Sweep-line algorithm for constrained Delaunay triangulation', * International Journal of Geographical Information Science * - * "FlipScan" Constrained Edge Algorithm invented by Thomas ?hl?n, thahlen@gmail.com + * "FlipScan" Constrained Edge Algorithm invented by Thomas Åhlén, thahlen@gmail.com */ -#ifndef SWEEP_H -#define SWEEP_H +#pragma once #include @@ -172,6 +171,7 @@ private: // Decision-making about when to Fill hole. // Contributed by ToolmakerSteve2 bool LargeHole_DontFill(const Node* node) const; + bool AngleIsNegative(const Point* origin, const Point* pa, const Point* pb) const; bool AngleExceeds90Degrees(const Point* origin, const Point* pa, const Point* pb) const; bool AngleExceedsPlus90DegreesOrIsNegative(const Point* origin, const Point* pa, const Point* pb) const; double Angle(const Point* origin, const Point* pa, const Point* pb) const; @@ -281,5 +281,3 @@ private: }; } - -#endif diff --git a/contrib/poly2tri/poly2tri/sweep/sweep_context.cc b/contrib/poly2tri/poly2tri/sweep/sweep_context.cc index a9f1fdf8e..7b9432feb 100644 --- a/contrib/poly2tri/poly2tri/sweep/sweep_context.cc +++ b/contrib/poly2tri/poly2tri/sweep/sweep_context.cc @@ -1,6 +1,6 @@ /* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ + * Poly2Tri Copyright (c) 2009-2022, Poly2Tri Contributors + * https://github.com/jhasse/poly2tri * * All rights reserved. * @@ -34,13 +34,13 @@ namespace p2t { -SweepContext::SweepContext(const std::vector& polyline) : points_(polyline), - front_(0), - head_(0), - tail_(0), - af_head_(0), - af_middle_(0), - af_tail_(0) +SweepContext::SweepContext(std::vector polyline) : points_(std::move(polyline)), + front_(nullptr), + head_(nullptr), + tail_(nullptr), + af_head_(nullptr), + af_middle_(nullptr), + af_tail_(nullptr) { InitEdges(points_); } @@ -48,8 +48,8 @@ SweepContext::SweepContext(const std::vector& polyline) : points_(polyli void SweepContext::AddHole(const std::vector& polyline) { InitEdges(polyline); - for(unsigned int i = 0; i < polyline.size(); i++) { - points_.push_back(polyline[i]); + for (auto i : polyline) { + points_.push_back(i); } } @@ -73,8 +73,8 @@ void SweepContext::InitTriangulation() double ymax(points_[0]->y), ymin(points_[0]->y); // Calculate bounds. - for (unsigned int i = 0; i < points_.size(); i++) { - Point& p = *points_[i]; + for (auto& point : points_) { + Point& p = *point; if (p.x > xmax) xmax = p.x; if (p.x < xmin) @@ -87,8 +87,8 @@ void SweepContext::InitTriangulation() double dx = kAlpha * (xmax - xmin); double dy = kAlpha * (ymax - ymin); - head_ = new Point(xmax + dx, ymin - dy); - tail_ = new Point(xmin - dx, ymin - dy); + head_ = new Point(xmin - dx, ymin - dy); + tail_ = new Point(xmax + dx, ymin - dy); // Sort points along y-axis std::sort(points_.begin(), points_.end(), cmp); @@ -114,18 +114,17 @@ void SweepContext::AddToMap(Triangle* triangle) map_.push_back(triangle); } -Node& SweepContext::LocateNode(const Point& point) +Node* SweepContext::LocateNode(const Point& point) { // TODO implement search tree - return *front_->LocateNode(point.x); + return front_->LocateNode(point.x); } -void SweepContext::CreateAdvancingFront(const std::vector& nodes) +void SweepContext::CreateAdvancingFront() { - (void) nodes; // Initial triangle - Triangle* triangle = new Triangle(*points_[0], *tail_, *head_); + Triangle* triangle = new Triangle(*points_[0], *head_, *tail_); map_.push_back(triangle); @@ -172,7 +171,7 @@ void SweepContext::MeshClean(Triangle& triangle) Triangle *t = triangles.back(); triangles.pop_back(); - if (t != NULL && !t->IsInterior()) { + if (t != nullptr && !t->IsInterior()) { t->IsInterior(true); triangles_.push_back(t); for (int i = 0; i < 3; i++) { @@ -195,17 +194,13 @@ SweepContext::~SweepContext() delete af_middle_; delete af_tail_; - typedef std::list type_list; - - for(type_list::iterator iter = map_.begin(); iter != map_.end(); ++iter) { - Triangle* ptr = *iter; - delete ptr; + for (auto ptr : map_) { + delete ptr; } - for(unsigned int i = 0; i < edge_list.size(); i++) { - delete edge_list[i]; + for (auto& i : edge_list) { + delete i; } - } -} +} // namespace p2t diff --git a/contrib/poly2tri/poly2tri/sweep/sweep_context.h b/contrib/poly2tri/poly2tri/sweep/sweep_context.h index ba0d06581..11d573944 100644 --- a/contrib/poly2tri/poly2tri/sweep/sweep_context.h +++ b/contrib/poly2tri/poly2tri/sweep/sweep_context.h @@ -1,6 +1,6 @@ /* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ + * Poly2Tri Copyright (c) 2009-2022, Poly2Tri Contributors + * https://github.com/jhasse/poly2tri * * All rights reserved. * @@ -29,8 +29,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SWEEP_CONTEXT_H -#define SWEEP_CONTEXT_H +#pragma once #include #include @@ -52,7 +51,7 @@ class SweepContext { public: /// Constructor -SweepContext(const std::vector& polyline); +explicit SweepContext(std::vector polyline); /// Destructor ~SweepContext(); @@ -66,11 +65,11 @@ Point* tail() const; size_t point_count() const; -Node& LocateNode(const Point& point); +Node* LocateNode(const Point& point); void RemoveNode(Node* node); -void CreateAdvancingFront(const std::vector& nodes); +void CreateAdvancingFront(); /// Try to map a node to all sides of this triangle that don't have a neighbor void MapTriangleToNodes(Triangle& t); @@ -103,15 +102,16 @@ struct Basin { double width; bool left_highest; - Basin() : left_node(NULL), bottom_node(NULL), right_node(NULL), width(0.0), left_highest(false) + Basin() + : left_node(nullptr), bottom_node(nullptr), right_node(nullptr), width(0.0), left_highest(false) { } void Clear() { - left_node = NULL; - bottom_node = NULL; - right_node = NULL; + left_node = nullptr; + bottom_node = nullptr; + right_node = nullptr; width = 0.0; left_highest = false; } @@ -182,5 +182,3 @@ inline Point* SweepContext::tail() const } } - -#endif diff --git a/contrib/tinyusdz/README.md b/contrib/tinyusdz/README.md new file mode 100644 index 000000000..b32feca6f --- /dev/null +++ b/contrib/tinyusdz/README.md @@ -0,0 +1,13 @@ +# tinyusdz +"tinyusdz" C++ project provides USD/USDA/UDSC/UDSZ 3D model file format suport + +## Automatic repo clone +tinyusdz repo is automatically cloned. Users who haven't opted-in to USD support +won't be burdened with the extra download volume. + +To update te git commit hash pulled down, modify `TINYUSDZ_GIT_TAG` in file + `code/CMakeLists.txt` + +## Notes +Couldn't leverage tinyusdz CMakeLists.txt. Fell back to compiling source files specified in +"android" example. diff --git a/contrib/tinyusdz/assimp_tinyusdz_logging.inc b/contrib/tinyusdz/assimp_tinyusdz_logging.inc new file mode 100644 index 000000000..d1e8dd2b6 --- /dev/null +++ b/contrib/tinyusdz/assimp_tinyusdz_logging.inc @@ -0,0 +1,57 @@ +/** + * Usage + * Add line below all other #include statements: + * #include "../../../assimp_tinyusdz_logging.inc" + * to files: + * - contrib/tinyusdz/tinyusdz_repo/src/tydra/render-data.cc + * - contrib/tinyusdz/tinyusdz_repo/src/tydra/scene-access.cc + */ +#pragma once + +#if defined(__ANDROID__) +#include +#include + +#define TINYUSDZLOGT(tag, ...) ((void)__android_log_print(ANDROID_LOG_DEBUG, tag, __VA_ARGS__)) +#define TINYUSDZLOG0(tag, ...) ((void)__android_log_print(ANDROID_LOG_DEFAULT, tag, __VA_ARGS__)) +#define TINYUSDZLOGD(tag, ...) ((void)__android_log_print(ANDROID_LOG_DEBUG, tag, __VA_ARGS__)) +#define TINYUSDZLOGI(tag, ...) ((void)__android_log_print(ANDROID_LOG_INFO, tag, __VA_ARGS__)) +#define TINYUSDZLOGW(tag, ...) ((void)__android_log_print(ANDROID_LOG_WARN, tag, __VA_ARGS__)) +#define TINYUSDZLOGE(tag, ...) ((void)__android_log_print(ANDROID_LOG_ERROR, tag, __VA_ARGS__)) +#else +#define TINYUSDZLOGT(tag, ...) +#define TINYUSDZLOG0(tag, ...) +#define TINYUSDZLOGD(tag, ...) +#define TINYUSDZLOGI(tag, ...) +#define TINYUSDZLOGW(tag, ...) +#define TINYUSDZLOGE(tag, ...) +#endif // #if defined(__ANDROID__) + +#if defined(TINYUSDZ_LOCAL_DEBUG_PRINT) +#if defined(__ANDROID__) +#if defined(ASSIMP_USD_VERBOSE_LOGS) +// Works well but _extremely_ verbose +#define DCOUT(x) \ + do { \ + std::stringstream ss; \ + ss << __FILE__ << ":" << __func__ << ":" \ + << std::to_string(__LINE__) << " " << x << "\n"; \ + TINYUSDZLOGE("tinyusdz", "%s", ss.str().c_str()); \ + } while (false) +#else // defined(ASSIMP_USD_VERBOSE_LOGS) +// Silent version +#define DCOUT(x) \ + do { \ + std::stringstream ss; \ + ss << __FILE__ << ":" << __func__ << ":" \ + << std::to_string(__LINE__) << " " << x << "\n"; \ + } while (false) +#endif // defined(ASSIMP_USD_VERBOSE_LOGS) +#else // defined(__ANDROID__) +#define DCOUT(x) \ + do { \ + std::cout << __FILE__ << ":" << __func__ << ":" \ + << std::to_string(__LINE__) << " " << x << "\n"; \ + } while (false) +#endif // #if defined(__ANDROID__) +#endif // #if defined(TINYUSDZ_LOCAL_DEBUG_PRINT) \ No newline at end of file diff --git a/contrib/tinyusdz/patches/README.md b/contrib/tinyusdz/patches/README.md new file mode 100644 index 000000000..a6b2040c3 --- /dev/null +++ b/contrib/tinyusdz/patches/README.md @@ -0,0 +1,40 @@ +# Tinyusdz patch files + +Pending acceptance of proposed changes upstream, need to resort to patching to keep things moving + +## Tinyusdz files needing patches + +### `tinyusdz_repo/src/external/stb_image_resize2.h` +Without patch, build will fail for armeabi-v7a ABI via android NDK + +Add `#elif` block as indicated below around line `2407` +``` +#elif defined(STBIR_WASM) || (defined(STBIR_NEON) && defined(_MSC_VER) && defined(_M_ARM)) // WASM or 32-bit ARM on MSVC/clang +... +#elif defined(STBIR_NEON) && (defined(__ANDROID__) && defined(__arm__)) // 32-bit ARM on android NDK + + static stbir__inline void stbir__half_to_float_SIMD(float * output, stbir__FP16 const * input) + { + // TODO: this stub is just to allow build on armeabi-v7a via android NDK + } + + static stbir__inline void stbir__float_to_half_SIMD(stbir__FP16 * output, float const * input) + { + // TODO: this stub is just to allow build on armeabi-v7a via android NDK + } + + static stbir__inline float stbir__half_to_float( stbir__FP16 h ) + { + // TODO: this stub is just to allow build on armeabi-v7a via android NDK + return 0; + } + + static stbir__inline stbir__FP16 stbir__float_to_half( float f ) + { + // TODO: this stub is just to allow build on armeabi-v7a via android NDK + return 0; + } + +#elif defined(STBIR_NEON) && defined(_MSC_VER) && defined(_M_ARM64) && !defined(__clang__) // 64-bit ARM on MSVC (not clang) +``` + diff --git a/contrib/tinyusdz/patches/tinyusdz.patch b/contrib/tinyusdz/patches/tinyusdz.patch new file mode 100644 index 000000000..e84e9f8fe --- /dev/null +++ b/contrib/tinyusdz/patches/tinyusdz.patch @@ -0,0 +1,42 @@ +diff -rupN -x .git autoclone/tinyusdz_repo-src/src/external/stb_image_resize2.h tinyusdz_repo_patch/src/external/stb_image_resize2.h +--- autoclone/tinyusdz_repo-src/src/external/stb_image_resize2.h 2024-07-09 21:29:48.556969900 -0700 ++++ tinyusdz_repo_patch/src/external/stb_image_resize2.h 2024-07-09 23:03:47.379316700 -0700 +@@ -2404,6 +2404,38 @@ static stbir__inline stbir_uint8 stbir__ + } + } + ++#elif defined(STBIR_NEON) && (defined(__ANDROID__) && defined(__arm__)) // 32-bit ARM on android NDK ++ ++ // TODO As of Apr 2024, tinyusdz doesn't support building on armeabi-v7a (32 bit arm) for android ++ // (falls through to arm64 block and build fails) ++ // ++ // For assimp integration, the image processing utilities aren't used at all, so it's safe to ++ // essentially replace the functions with dummy no-ops. ++ // ++ // This will need to be done manually whenever the tinyusdz source files are updated in assimp, ++ // as it seems unlikely this will be fixed in the tinyusdz project ++ static stbir__inline void stbir__half_to_float_SIMD(float * output, stbir__FP16 const * input) ++ { ++ // TODO: this stub is just to allow build on armeabi-v7a via android NDK ++ } ++ ++ static stbir__inline void stbir__float_to_half_SIMD(stbir__FP16 * output, float const * input) ++ { ++ // TODO: this stub is just to allow build on armeabi-v7a via android NDK ++ } ++ ++ static stbir__inline float stbir__half_to_float( stbir__FP16 h ) ++ { ++ // TODO: this stub is just to allow build on armeabi-v7a via android NDK ++ return 0; ++ } ++ ++ static stbir__inline stbir__FP16 stbir__float_to_half( float f ) ++ { ++ // TODO: this stub is just to allow build on armeabi-v7a via android NDK ++ return 0; ++ } ++ + #elif defined(STBIR_NEON) && defined(_MSC_VER) && defined(_M_ARM64) && !defined(__clang__) // 64-bit ARM on MSVC (not clang) + + static stbir__inline void stbir__half_to_float_SIMD(float * output, stbir__FP16 const * input) diff --git a/contrib/zip/.gitattributes b/contrib/zip/.gitattributes new file mode 100644 index 000000000..15a5c5809 --- /dev/null +++ b/contrib/zip/.gitattributes @@ -0,0 +1 @@ +*.h linguist-language=C diff --git a/contrib/zip/.gitignore b/contrib/zip/.gitignore new file mode 100644 index 000000000..a5900dbcd --- /dev/null +++ b/contrib/zip/.gitignore @@ -0,0 +1,60 @@ +/build/ +/test/build/ +/xcodeproj/ +/infer-out/ +.vscode/ +Testing/ + +# Object files +*.o +*.ko +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib +*.suo + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Temporary +*.swp +.DS_Store + +# CMake +CMakeScripts +*.cmake + +# Xcode +*.build +*.xcodeproj +zip.sln +zip.vcxproj.filters +zip.vcxproj +ALL_BUILD.vcxproj.filters +ALL_BUILD.vcxproj +CMakeFiles/ +zip.dir/ +test/test.exe.vcxproj.filters +test/test.exe.vcxproj +test/test.exe.dir/ + diff --git a/contrib/zip/.travis.sh b/contrib/zip/.travis.sh deleted file mode 100755 index 9cb03ee87..000000000 --- a/contrib/zip/.travis.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -# -# Build script for travis-ci.org builds. -# -if [ $ANALYZE = "true" ] && [ "$CC" = "clang" ]; then - # scan-build -h - scan-build cmake -G "Unix Makefiles" - scan-build -enable-checker security.FloatLoopCounter \ - -enable-checker security.insecureAPI.UncheckedReturn \ - --status-bugs -v \ - make -j 8 \ - make -j 8 test -else - cmake -DCMAKE_BUILD_TYPE=Debug -DSANITIZE_ADDRESS=On -DCMAKE_INSTALL_PREFIX=_install - make -j 8 - make install - ASAN_OPTIONS=detect_leaks=0 LSAN_OPTIONS=verbosity=1:log_threads=1 ctest -V -fi diff --git a/contrib/zip/.travis.yml b/contrib/zip/.travis.yml deleted file mode 100644 index 42f84dd25..000000000 --- a/contrib/zip/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -language: c -addons: - apt: - packages: &1 - - lcov -# Compiler selection -compiler: - - clang - - gcc -env: - - ANALYZE=false - - ANALYZE=true -# Build steps -script: - - ./.travis.sh -after_success: - # Creating report - - cmake -DENABLE_COVERAGE=ON - - make - - make test - # Uploading report to CodeCov - - bash <(curl -s https://codecov.io/bash) diff --git a/contrib/zip/CMakeLists.txt b/contrib/zip/CMakeLists.txt index bba4e7bde..8f10e0c10 100644 --- a/contrib/zip/CMakeLists.txt +++ b/contrib/zip/CMakeLists.txt @@ -1,27 +1,47 @@ -cmake_minimum_required(VERSION 3.4) +cmake_minimum_required(VERSION 3.14) project(zip LANGUAGES C - VERSION "0.1.19") + VERSION "0.3.0") set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) set(CMAKE_VERBOSE_MAKEFILE ON) -option(CMAKE_DISABLE_TESTING "Disable test creation" OFF) + +# Enable building tests only if the project is being built as a standalone one +if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + option(CMAKE_DISABLE_TESTING "Disable test creation" OFF) +else () + option(CMAKE_DISABLE_TESTING "Disable test creation" ON) +endif () + +option(CMAKE_ENABLE_SANITIZERS "Enable zip sanitizers" OFF) +option(ZIP_STATIC_PIC "Build static zip with PIC" ON) +option(ZIP_BUILD_DOCS "Generate API documentation with Doxygen" OFF) + +if(ZIP_ENABLE_SHARABLE_FILE_OPEN) + add_definitions(-DZIP_ENABLE_SHARABLE_FILE_OPEN) +endif() + +if(CMAKE_SIZEOF_VOID_P EQUAL 4) + # large file support + add_definitions(-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64) +endif() # zip set(SRC src/miniz.h src/zip.h src/zip.c) -# this is the "object library" target: compiles the sources only once -add_library(OBJLIB OBJECT ${SRC}) -# shared libraries need PIC -set_property(TARGET OBJLIB PROPERTY POSITION_INDEPENDENT_CODE 1) +add_library(${PROJECT_NAME} ${SRC}) +add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) -# static and shared libraries built from the same object files -if (BUILD_SHARED_LIBS) - add_library(${PROJECT_NAME} SHARED $) - include(GenerateExportHeader) - generate_export_header(${PROJECT_NAME}) -else() - add_library(${PROJECT_NAME} STATIC $) +if(ZIP_STATIC_PIC) + set_property(TARGET ${PROJECT_NAME} PROPERTY POSITION_INDEPENDENT_CODE 1) +endif() + +set_property(TARGET ${PROJECT_NAME} PROPERTY C_VISIBILITY_PRESET hidden) +if(BUILD_SHARED_LIBS) + target_compile_definitions(${PROJECT_NAME} + PUBLIC ZIP_SHARED + PRIVATE ZIP_BUILD_SHARED + ) endif() target_include_directories(${PROJECT_NAME} PUBLIC @@ -33,24 +53,26 @@ target_include_directories(${PROJECT_NAME} PUBLIC if (NOT CMAKE_DISABLE_TESTING) enable_testing() add_subdirectory(test) +endif() +if (CMAKE_ENABLE_SANITIZERS) find_package(Sanitizers) - add_sanitizers(${PROJECT_NAME} ${test_out}) + add_sanitizers(${PROJECT_NAME}) endif() +set(CMAKE_C_STANDARD 90) if (MSVC) # Use secure functions by default and suppress warnings about "deprecated" functions set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT=1") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_NONSTDC_NO_WARNINGS=1 /D _CRT_SECURE_NO_WARNINGS=1") + elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -Wextra -Werror -pedantic -Wno-deprecated") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror -pedantic") endif (MSVC) #### -# Installation (https://github.com/forexample/package-example) { - set(CONFIG_INSTALL_DIR "lib/cmake/${PROJECT_NAME}") set(INCLUDE_INSTALL_DIR "include") @@ -62,7 +84,7 @@ set(PROJECT_CONFIG "${GENERATED_DIR}/${PROJECT_NAME}Config.cmake") set(TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets") set(NAMESPACE "${PROJECT_NAME}::") -# Include module with fuction 'write_basic_package_version_file' +# Include module with function 'write_basic_package_version_file' include(CMakePackageConfigHelpers) # Note: PROJECT_VERSION is used as a VERSION @@ -90,8 +112,6 @@ install( DESTINATION "${CONFIG_INSTALL_DIR}" ) -# } - install(TARGETS ${PROJECT_NAME} EXPORT ${TARGETS_EXPORT_NAME} RUNTIME DESTINATION bin @@ -112,8 +132,8 @@ if(NOT TARGET uninstall) COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake/cmake_uninstall.cmake) endif() -find_package(Doxygen) -if(DOXYGEN_FOUND) +if(ZIP_BUILD_DOCS) + find_package(Doxygen REQUIRED) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY) add_custom_target(doc ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile diff --git a/contrib/zip/CONTRIBUTING.md b/contrib/zip/CONTRIBUTING.md new file mode 100644 index 000000000..bec8e0971 --- /dev/null +++ b/contrib/zip/CONTRIBUTING.md @@ -0,0 +1,9 @@ +# Contribution Rules/Coding Standards +No need to throw away your coding style, just do your best to follow default clang-format style. +Apply `clang-format` to the source files before commit: +```sh +for file in $(git ls-files | \grep -E '\.(c|h)$' | \grep -v -- '#') +do + clang-format -i $file +done +``` diff --git a/contrib/zip/Doxyfile.in b/contrib/zip/Doxyfile.in new file mode 100644 index 000000000..7f03b1833 --- /dev/null +++ b/contrib/zip/Doxyfile.in @@ -0,0 +1,2480 @@ +# Doxyfile 1.8.14 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = zip + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = @ZIP_VERSION@ + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = doc + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = @CMAKE_CURRENT_SOURCE_DIR@/src + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines (in the resulting output). You can put ^^ in the value part of an +# alias to insert a newline as if a physical newline was in the original file. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 0. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 0 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = YES + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = YES + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO, these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = YES + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = YES + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = YES + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = YES + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = NO + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = @CMAKE_CURRENT_SOURCE_DIR@/src + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: https://www.gnu.org/software/libiconv/) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f \ + *.for \ + *.tcl \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = NO + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = NO + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = NO + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via Javascript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have Javascript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: https://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop on Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://doc.qt.io/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://doc.qt.io/qt-4.8/qthelpproject.html#virtual-folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://doc.qt.io/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANSPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. +# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/ + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /