diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index db152b813..b6cfae367 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -7,10 +7,10 @@ on: branches: [ master ] permissions: - contents: read # to fetch code (actions/checkout) + contents: write # to fetch code (actions/checkout),and release jobs: - job: + build: name: ${{ matrix.name }}-build-and-test runs-on: ${{ matrix.os }} strategy: @@ -70,12 +70,12 @@ jobs: - name: Set Windows specific CMake arguments if: contains(matrix.name, 'windows') 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" + run: echo ":set-output name=args::=-DASSIMP_BUILD_ASSIMP_TOOLS=1 -DASSIMP_BUILD_ASSIMP_VIEW=1" >> $GITHUB_OUTPUT - name: Set Hunter specific CMake arguments if: contains(matrix.name, 'hunter') id: hunter_extra_cmake_args - run: echo "::set-output name=args::-DBUILD_SHARED_LIBS=OFF -DASSIMP_HUNTER_ENABLED=ON -DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/cmake/polly/${{ matrix.toolchain }}.cmake" + run: echo "args=-DBUILD_SHARED_LIBS=OFF -DASSIMP_HUNTER_ENABLED=ON -DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/cmake/polly/${{ matrix.toolchain }}.cmake" >> $GITHUB_OUTPUT - name: configure and build uses: lukka/run-cmake@v3 @@ -92,7 +92,7 @@ jobs: - name: Exclude certain tests in Hunter specific builds if: contains(matrix.name, 'hunter') id: hunter_extra_test_args - run: echo "::set-output name=args::--gtest_filter=-utOpenGEXImportExport.Importissue1340_EmptyCameraObject:utColladaZaeImportExport.importBlenFromFileTest" + run: echo "args=--gtest_filter=-utOpenGEXImportExport.Importissue1340_EmptyCameraObject:utColladaZaeImportExport.importBlenFromFileTest" >> $GITHUB_OUTPUT - name: test run: cd build/bin && ./unit ${{ steps.hunter_extra_test_args.outputs.args }} @@ -101,5 +101,89 @@ jobs: - uses: actions/upload-artifact@v4 if: matrix.name == 'windows-msvc' with: - name: 'assimp-bins-${{ matrix.name }}-${{ github.sha }}' - path: build/bin + name: 'assimp-bins-${{ matrix.name }}' + path: build/bin/assimp*.exe + + - uses: marvinpinto/action-automatic-releases@latest + if: contains(matrix.name, 'windows-msvc-hunter') + with: + repo_token: "${{ secrets.GITHUB_TOKEN }}" + automatic_release_tag: "master" + prerelease: true + title: "AutoRelease" + files: | + build/bin/assimp*.exe + + create-release: + needs: [build] + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/') + steps: + - id: create-release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: '${{secrets.GITHUB_TOKEN}}' + with: + tag_name: '${{github.ref}}' + release_name: 'Release ${{github.ref}}' + draft: false + prerelease: true + - run: | + echo '${{steps.create-release.outputs.upload_url}}' > release_upload_url.txt + - uses: actions/upload-artifact@v4 + with: + name: create-release + path: release_upload_url.txt + + upload-release: + strategy: + matrix: + name: [ubuntu-latest-g++, macos-latest-clang++, windows-latest-cl.exe, ubuntu-latest-clang++, ubuntu-gcc-hunter, macos-clang-hunter, windows-msvc-hunter] + # For Windows msvc, for Linux and macOS let's use the clang compiler, use gcc for Linux. + include: + - name: windows-latest-cl.exe + os: windows-latest + cxx: cl.exe + cc: cl.exe + - name: ubuntu-latest-clang++ + os: ubuntu-latest + cxx: clang++ + cc: clang + - name: macos-latest-clang++ + os: macos-latest + cxx: clang++ + cc: clang + - name: ubuntu-latest-g++ + os: ubuntu-latest + cxx: g++ + cc: gcc + - name: ubuntu-gcc-hunter + os: ubuntu-latest + toolchain: ninja-gcc-cxx17-fpic + - name: macos-clang-hunter + os: macos-latest + toolchain: ninja-clang-cxx17-fpic + - name: windows-msvc-hunter + os: windows-latest + toolchain: ninja-vs-win64-cxx17 + + needs: [create-release] + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/') + steps: + - uses: softprops/action-gh-release@v2 + with: + name: create-release + - id: upload-url + run: | + echo "url=$(cat create-release/release_upload_url.txt)" >> $GITHUB_OUTPUT + - uses: actions/download-artifact@v4 + with: + name: 'assimp-bins-${{ matrix.name }}-${{ github.sha }}' + - uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: '${{secrets.GITHUB_TOKEN}}' + with: + files: | + *.zip + 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 718a251e4..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) @@ -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() @@ -636,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) 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/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/D3MFExporter.cpp b/code/AssetLib/3MF/D3MFExporter.cpp index 6caa4c84e..6c09f097d 100644 --- a/code/AssetLib/3MF/D3MFExporter.cpp +++ b/code/AssetLib/3MF/D3MFExporter.cpp @@ -249,10 +249,10 @@ void D3MFExporter::writeBaseMaterials() { if (color.r <= 1 && color.g <= 1 && color.b <= 1 && color.a <= 1) { hexDiffuseColor = ai_rgba2hex( - (int)((ai_real)color.r) * 255, - (int)((ai_real)color.g) * 255, - (int)((ai_real)color.b) * 255, - (int)((ai_real)color.a) * 255, + (int)(((ai_real)color.r) * 255), + (int)(((ai_real)color.g) * 255), + (int)(((ai_real)color.b) * 255), + (int)(((ai_real)color.a) * 255), true); } else { diff --git a/code/AssetLib/AMF/AMFImporter.cpp b/code/AssetLib/AMF/AMFImporter.cpp index 42f9664be..7861c592e 100644 --- a/code/AssetLib/AMF/AMFImporter.cpp +++ b/code/AssetLib/AMF/AMFImporter.cpp @@ -384,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_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/ASE/ASEParser.cpp b/code/AssetLib/ASE/ASEParser.cpp index 02d21f41b..c9bbe3ca6 100644 --- a/code/AssetLib/ASE/ASEParser.cpp +++ b/code/AssetLib/ASE/ASEParser.cpp @@ -422,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); @@ -580,14 +580,14 @@ void Parser::ParseLV2MaterialBlock(ASE::Material &mat) { } // material transparency if (TokenMatch(mFilePtr, "MATERIAL_TRANSPARENCY", 21)) { - ParseLV4MeshFloat(mat.mTransparency); + ParseLV4MeshReal(mat.mTransparency); mat.mTransparency = ai_real(1.0) - mat.mTransparency; continue; } // material self illumination if (TokenMatch(mFilePtr, "MATERIAL_SELFILLUM", 18)) { ai_real f = 0.0; - ParseLV4MeshFloat(f); + ParseLV4MeshReal(f); mat.mEmissive.r = f; mat.mEmissive.g = f; @@ -596,7 +596,7 @@ void Parser::ParseLV2MaterialBlock(ASE::Material &mat) { } // material shininess if (TokenMatch(mFilePtr, "MATERIAL_SHINE", 14)) { - ParseLV4MeshFloat(mat.mSpecularExponent); + ParseLV4MeshReal(mat.mSpecularExponent); mat.mSpecularExponent *= 15; continue; } @@ -607,7 +607,7 @@ void Parser::ParseLV2MaterialBlock(ASE::Material &mat) { } // material shininess strength if (TokenMatch(mFilePtr, "MATERIAL_SHINESTRENGTH", 22)) { - ParseLV4MeshFloat(mat.mShininessStrength); + ParseLV4MeshReal(mat.mShininessStrength); continue; } // diffuse color map @@ -731,32 +731,32 @@ void Parser::ParseLV3MapBlock(Texture &map) { } // offset on the u axis if (TokenMatch(mFilePtr, "UVW_U_OFFSET", 12)) { - ParseLV4MeshFloat(map.mOffsetU); + ParseLV4MeshReal(map.mOffsetU); continue; } // offset on the v axis if (TokenMatch(mFilePtr, "UVW_V_OFFSET", 12)) { - ParseLV4MeshFloat(map.mOffsetV); + ParseLV4MeshReal(map.mOffsetV); continue; } // tiling on the u axis if (TokenMatch(mFilePtr, "UVW_U_TILING", 12)) { - ParseLV4MeshFloat(map.mScaleU); + ParseLV4MeshReal(map.mScaleU); continue; } // tiling on the v axis if (TokenMatch(mFilePtr, "UVW_V_TILING", 12)) { - ParseLV4MeshFloat(map.mScaleV); + ParseLV4MeshReal(map.mScaleV); continue; } // rotation around the z-axis if (TokenMatch(mFilePtr, "UVW_ANGLE", 9)) { - ParseLV4MeshFloat(map.mRotation); + ParseLV4MeshReal(map.mRotation); continue; } // map blending factor if (TokenMatch(mFilePtr, "MAP_AMOUNT", 10)) { - ParseLV4MeshFloat(map.mTextureBlend); + ParseLV4MeshReal(map.mTextureBlend); continue; } } @@ -895,15 +895,15 @@ void Parser::ParseLV2CameraSettingsBlock(ASE::Camera &camera) { if ('*' == *mFilePtr) { ++mFilePtr; if (TokenMatch(mFilePtr, "CAMERA_NEAR", 11)) { - ParseLV4MeshFloat(camera.mNear); + ParseLV4MeshReal(camera.mNear); continue; } if (TokenMatch(mFilePtr, "CAMERA_FAR", 10)) { - ParseLV4MeshFloat(camera.mFar); + ParseLV4MeshReal(camera.mFar); continue; } if (TokenMatch(mFilePtr, "CAMERA_FOV", 10)) { - ParseLV4MeshFloat(camera.mFOV); + ParseLV4MeshReal(camera.mFOV); continue; } } @@ -922,15 +922,15 @@ void Parser::ParseLV2LightSettingsBlock(ASE::Light &light) { continue; } if (TokenMatch(mFilePtr, "LIGHT_INTENS", 12)) { - ParseLV4MeshFloat(light.mIntensity); + ParseLV4MeshReal(light.mIntensity); continue; } if (TokenMatch(mFilePtr, "LIGHT_HOTSPOT", 13)) { - ParseLV4MeshFloat(light.mAngle); + ParseLV4MeshReal(light.mAngle); continue; } if (TokenMatch(mFilePtr, "LIGHT_FALLOFF", 13)) { - ParseLV4MeshFloat(light.mFalloff); + ParseLV4MeshReal(light.mFalloff); continue; } } @@ -1038,7 +1038,7 @@ void Parser::ParseLV3ScaleAnimationBlock(ASE::Animation &anim) { 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; } } @@ -1077,7 +1077,7 @@ void Parser::ParseLV3PosAnimationBlock(ASE::Animation &anim) { 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; } } @@ -1118,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); } @@ -1163,23 +1163,23 @@ void Parser::ParseLV2NodeTransformBlock(ASE::BaseNode &mesh) { // fourth row of the transformation matrix - and also the // only information here that is interesting for targets if (TokenMatch(mFilePtr, "TM_ROW3", 7)) { - ParseLV4MeshFloatTriple((mode == 1 ? mesh.mTransform[3] : &mesh.mTargetPosition.x)); + ParseLV4MeshRealTriple((mode == 1 ? mesh.mTransform[3] : &mesh.mTargetPosition.x)); continue; } if (mode == 1) { // first row of the transformation matrix if (TokenMatch(mFilePtr, "TM_ROW0", 7)) { - ParseLV4MeshFloatTriple(mesh.mTransform[0]); + ParseLV4MeshRealTriple(mesh.mTransform[0]); continue; } // second row of the transformation matrix if (TokenMatch(mFilePtr, "TM_ROW1", 7)) { - ParseLV4MeshFloatTriple(mesh.mTransform[1]); + ParseLV4MeshRealTriple(mesh.mTransform[1]); continue; } // third row of the transformation matrix if (TokenMatch(mFilePtr, "TM_ROW2", 7)) { - ParseLV4MeshFloatTriple(mesh.mTransform[2]); + ParseLV4MeshRealTriple(mesh.mTransform[2]); continue; } // inherited position axes @@ -1414,7 +1414,7 @@ void Parser::ParseLV4MeshBonesVertices(unsigned int iNumVertices, ASE::Mesh &mes // --- ignored ai_real afVert[3]; - ParseLV4MeshFloatTriple(afVert); + ParseLV4MeshRealTriple(afVert); std::pair pairOut; while (true) { @@ -1453,7 +1453,7 @@ void Parser::ParseLV3MeshVertexListBlock( aiVector3D vTemp; unsigned int iIndex; - ParseLV4MeshFloatTriple(&vTemp.x, iIndex); + ParseLV4MeshRealTriple(&vTemp.x, iIndex); if (iIndex >= iNumVertices) { LogWarning("Invalid vertex index. It will be ignored"); @@ -1506,7 +1506,7 @@ void Parser::ParseLV3MeshTListBlock(unsigned int iNumVertices, 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"); @@ -1657,7 +1657,7 @@ void Parser::ParseLV3MeshNormalListBlock(ASE::Mesh &sMesh) { ++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; @@ -1679,7 +1679,7 @@ void Parser::ParseLV3MeshNormalListBlock(ASE::Mesh &sMesh) { } 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"); @@ -1844,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 @@ -1854,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) { @@ -1862,7 +1880,7 @@ void Parser::ParseLV4MeshFloatTriple(ai_real *apOut) { } } // ------------------------------------------------------------------------------------------------ -void Parser::ParseLV4MeshFloat(ai_real &fOut) { +void Parser::ParseLV4MeshReal(ai_real &fOut) { // skip spaces and tabs if (!SkipSpaces(&mFilePtr, mEnd)) { // LOG @@ -1875,6 +1893,19 @@ void Parser::ParseLV4MeshFloat(ai_real &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(&mFilePtr, mEnd)) { diff --git a/code/AssetLib/ASE/ASEParser.h b/code/AssetLib/ASE/ASEParser.h index 9c5cd39b1..916605790 100644 --- a/code/AssetLib/ASE/ASEParser.h +++ b/code/AssetLib/ASE/ASEParser.h @@ -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 diff --git a/code/AssetLib/Collada/ColladaParser.cpp b/code/AssetLib/Collada/ColladaParser.cpp index e7f91b5fe..0741b3c73 100644 --- a/code/AssetLib/Collada/ColladaParser.cpp +++ b/code/AssetLib/Collada/ColladaParser.cpp @@ -968,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); } } } @@ -1010,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); } } } @@ -1170,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); @@ -1198,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); } } } @@ -1265,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); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/FBX/FBXConverter.cpp b/code/AssetLib/FBX/FBXConverter.cpp index 234931cbe..fd54c63f4 100644 --- a/code/AssetLib/FBX/FBXConverter.cpp +++ b/code/AssetLib/FBX/FBXConverter.cpp @@ -2128,6 +2128,10 @@ void FBXConverter::SetTextureProperties(aiMaterial *out_mat, const TextureMap &_ TrySetTextureProperties(out_mat, _textures, "Maya|emissionColor", aiTextureType_EMISSION_COLOR, mesh); TrySetTextureProperties(out_mat, _textures, "Maya|metalness", aiTextureType_METALNESS, mesh); TrySetTextureProperties(out_mat, _textures, "Maya|diffuseRoughness", aiTextureType_DIFFUSE_ROUGHNESS, mesh); + TrySetTextureProperties(out_mat, _textures, "Maya|base", aiTextureType_MAYA_BASE, mesh); + TrySetTextureProperties(out_mat, _textures, "Maya|specular", aiTextureType_MAYA_SPECULAR, mesh); + TrySetTextureProperties(out_mat, _textures, "Maya|specularColor", aiTextureType_MAYA_SPECULAR_COLOR, mesh); + TrySetTextureProperties(out_mat, _textures, "Maya|specularRoughness", aiTextureType_MAYA_SPECULAR_ROUGHNESS, mesh); // Maya stingray TrySetTextureProperties(out_mat, _textures, "Maya|TEX_color_map", aiTextureType_BASE_COLOR, mesh); diff --git a/code/AssetLib/FBX/FBXExporter.cpp b/code/AssetLib/FBX/FBXExporter.cpp index 3b1dd335e..79fa572af 100644 --- a/code/AssetLib/FBX/FBXExporter.cpp +++ b/code/AssetLib/FBX/FBXExporter.cpp @@ -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)); @@ -2486,6 +2487,57 @@ const std::map> transform_types = { {"GeometricScalingInverse", {"GeometricScalingInverse", 'i'}} }; +//add metadata to fbx property +void add_meta(FBX::Node& fbx_node, const aiNode* node){ + if(node->mMetaData == nullptr) return; + aiMetadata* meta = node->mMetaData; + for (unsigned int i = 0; i < meta->mNumProperties; ++i) { + aiString key = meta->mKeys[i]; + aiMetadataEntry* entry = &meta->mValues[i]; + switch (entry->mType) { + case AI_BOOL:{ + bool val = *static_cast(entry->mData); + fbx_node.AddP70bool(key.C_Str(), val); + break; + } + case AI_INT32:{ + int32_t val = *static_cast(entry->mData); + fbx_node.AddP70int(key.C_Str(), val); + break; + } + case AI_UINT64:{ + //use string to add uint64 + uint64_t val = *static_cast(entry->mData); + fbx_node.AddP70string(key.C_Str(), std::to_string(val).c_str()); + break; + } + case AI_FLOAT:{ + float val = *static_cast(entry->mData); + fbx_node.AddP70double(key.C_Str(), val); + break; + } + case AI_DOUBLE:{ + double val = *static_cast(entry->mData); + fbx_node.AddP70double(key.C_Str(), val); + break; + } + case AI_AISTRING:{ + aiString val = *static_cast(entry->mData); + fbx_node.AddP70string(key.C_Str(), val.C_Str()); + break; + } + case AI_AIMETADATA: { + //ignore + break; + } + default: + break; + } + + } + +} + // write a single model node to the stream void FBXExporter::WriteModelNode( StreamWriterLE& outstream, @@ -2553,6 +2605,7 @@ void FBXExporter::WriteModelNode( } } } + add_meta(p, node); m.AddChild(p); // not sure what these are for, 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 498e40b34..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(); diff --git a/code/AssetLib/MDL/MDLMaterialLoader.cpp b/code/AssetLib/MDL/MDLMaterialLoader.cpp index f68f8e23e..d0a2d5f79 100644 --- a/code/AssetLib/MDL/MDLMaterialLoader.cpp +++ b/code/AssetLib/MDL/MDLMaterialLoader.cpp @@ -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,10 +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) { + 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/Ply/PlyLoader.cpp b/code/AssetLib/Ply/PlyLoader.cpp index 0c2463f24..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 @@ -572,7 +578,7 @@ void PLYImporter::LoadFace(const PLY::Element *pcElement, const PLY::ElementInst 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]; @@ -585,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 @@ -679,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/STL/STLLoader.cpp b/code/AssetLib/STL/STLLoader.cpp index 3a9fc1b12..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; 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/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 3d309049e..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); diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index f4c293672..9b2708623 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -828,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 @@ -921,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) @@ -1171,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: @@ -1204,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) @@ -1445,7 +1575,7 @@ if(MSVC AND ASSIMP_INSTALL_PDB) COMPILE_PDB_NAME_DEBUG assimp${LIBRARY_SUFFIX}${CMAKE_DEBUG_POSTFIX} ) - IF(GENERATOR_IS_MULTI_CONFIG) + IF(is_multi_config) install(FILES ${Assimp_BINARY_DIR}/code/Debug/assimp${LIBRARY_SUFFIX}${CMAKE_DEBUG_POSTFIX}.pdb DESTINATION ${ASSIMP_LIB_INSTALL_DIR} CONFIGURATIONS Debug diff --git a/code/Common/Assimp.cpp b/code/Common/Assimp.cpp index bd1d6aedd..ef3ee7b5d 100644 --- a/code/Common/Assimp.cpp +++ b/code/Common/Assimp.cpp @@ -512,6 +512,11 @@ void aiGetMemoryRequirements(const C_STRUCT aiScene *pIn, ASSIMP_END_EXCEPTION_REGION(void); } +// ------------------------------------------------------------------------------------------------ +ASSIMP_API const C_STRUCT aiTexture *aiGetEmbeddedTexture(const C_STRUCT aiScene *pIn, const char *filename) { + return pIn->GetEmbeddedTexture(filename); +} + // ------------------------------------------------------------------------------------------------ ASSIMP_API aiPropertyStore *aiCreatePropertyStore(void) { return reinterpret_cast(new PropertyMap()); @@ -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(); } @@ -1268,7 +1273,6 @@ ASSIMP_API void aiQuaternionInterpolate( aiQuaternion::Interpolate(*dst, *start, *end, factor); } - // stb_image is a lightweight image loader. It is shared by: // - M3D import // - PBRT export @@ -1279,21 +1283,21 @@ ASSIMP_API void aiQuaternionInterpolate( #define ASSIMP_HAS_M3D ((!ASSIMP_BUILD_NO_EXPORT && !ASSIMP_BUILD_NO_M3D_EXPORTER) || !ASSIMP_BUILD_NO_M3D_IMPORTER) #ifndef STB_USE_HUNTER -# if ASSIMP_HAS_PBRT_EXPORT -# define ASSIMP_NEEDS_STB_IMAGE 1 -# elif ASSIMP_HAS_M3D -# define ASSIMP_NEEDS_STB_IMAGE 1 -# define STBI_ONLY_PNG -# endif +#if ASSIMP_HAS_PBRT_EXPORT +#define ASSIMP_NEEDS_STB_IMAGE 1 +#elif ASSIMP_HAS_M3D +#define ASSIMP_NEEDS_STB_IMAGE 1 +#define STBI_ONLY_PNG +#endif #endif // Ensure all symbols are linked correctly #if ASSIMP_NEEDS_STB_IMAGE - // Share stb_image's PNG loader with other importers/exporters instead of bringing our own copy. -# define STBI_ONLY_PNG -# ifdef ASSIMP_USE_STB_IMAGE_STATIC -# define STB_IMAGE_STATIC -# endif -# define STB_IMAGE_IMPLEMENTATION -# include "Common/StbCommon.h" +// Share stb_image's PNG loader with other importers/exporters instead of bringing our own copy. +#define STBI_ONLY_PNG +#ifdef ASSIMP_USE_STB_IMAGE_STATIC +#define STB_IMAGE_STATIC +#endif +#define STB_IMAGE_IMPLEMENTATION +#include "Common/StbCommon.h" #endif 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/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 7954d4d01..88fa49793 100644 --- a/code/Common/SceneCombiner.cpp +++ b/code/Common/SceneCombiner.cpp @@ -63,6 +63,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include namespace Assimp { @@ -252,6 +253,14 @@ void SceneCombiner::AttachToGraph(aiScene *master, std::vector &srcList, unsigned int flags) { if (nullptr == _dest) { + std::unordered_set uniqueScenes; + uniqueScenes.insert(master); + for (const auto &item : srcList) { + uniqueScenes.insert(item.scene); + } + for (const auto &item : uniqueScenes) { + delete item; + } return; } @@ -259,6 +268,7 @@ void SceneCombiner::MergeScenes(aiScene **_dest, aiScene *master, std::vectormFlags = src->mFlags; // source private data might be nullptr if the scene is user-allocated (i.e. for use with the export API) - if (dest->mPrivate != nullptr) { + if (src->mPrivate != nullptr) { ScenePriv(dest)->mPPStepsApplied = ScenePriv(src) ? ScenePriv(src)->mPPStepsApplied : 0; } } 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/Material/MaterialSystem.cpp b/code/Material/MaterialSystem.cpp index 7e2fb51b9..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, diff --git a/code/PostProcessing/CalcTangentsProcess.cpp b/code/PostProcessing/CalcTangentsProcess.cpp index abd0aee47..8fd063260 100644 --- a/code/PostProcessing/CalcTangentsProcess.cpp +++ b/code/PostProcessing/CalcTangentsProcess.cpp @@ -200,8 +200,10 @@ bool CalcTangentsProcess::ProcessMesh(aiMesh *pMesh, unsigned int meshIndex) { 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/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/ImproveCacheLocality.cpp b/code/PostProcessing/ImproveCacheLocality.cpp index c165b0114..e108354b1 100644 --- a/code/PostProcessing/ImproveCacheLocality.cpp +++ b/code/PostProcessing/ImproveCacheLocality.cpp @@ -380,3 +380,4 @@ ai_real ImproveCacheLocalityProcess::ProcessMesh(aiMesh *pMesh, unsigned int mes } } // namespace Assimp + 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/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/doc/Fileformats.md b/doc/Fileformats.md index 6fc0fe34c..50d2d5e69 100644 --- a/doc/Fileformats.md +++ b/doc/Fileformats.md @@ -60,6 +60,7 @@ __Importers__: - [STL](https://en.wikipedia.org/wiki/STL_(file_format)) - TER - UC +- [USD](https://en.wikipedia.org/wiki/Universal_Scene_Description) - VTA - X - [X3D](https://en.wikipedia.org/wiki/X3D) diff --git a/include/assimp/BaseImporter.h b/include/assimp/BaseImporter.h index 447784e75..167b254d6 100644 --- a/include/assimp/BaseImporter.h +++ b/include/assimp/BaseImporter.h @@ -277,7 +277,8 @@ public: // static utilities const std::string &pFile, const char *ext0, const char *ext1 = nullptr, - const char *ext2 = nullptr); + const char *ext2 = nullptr, + const char *ext3 = nullptr); // ------------------------------------------------------------------- /** @brief Check whether a file has one of the passed file extensions diff --git a/include/assimp/SpatialSort.h b/include/assimp/SpatialSort.h index e527b15a2..006234acb 100644 --- a/include/assimp/SpatialSort.h +++ b/include/assimp/SpatialSort.h @@ -79,7 +79,7 @@ public: unsigned int pElementOffset); /** Destructor */ - ~SpatialSort(); + ~SpatialSort() = default; // ------------------------------------------------------------------------------------ /** Sets the input data for the SpatialSort. This replaces existing data, if any. diff --git a/include/assimp/StringComparison.h b/include/assimp/StringComparison.h index 283d8ba6b..c4174c520 100644 --- a/include/assimp/StringComparison.h +++ b/include/assimp/StringComparison.h @@ -57,7 +57,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma GCC system_header #endif -#include #include #include diff --git a/include/assimp/Vertex.h b/include/assimp/Vertex.h index 62a531ce2..c0a6a836a 100644 --- a/include/assimp/Vertex.h +++ b/include/assimp/Vertex.h @@ -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, @@ -61,7 +60,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include -namespace Assimp { +namespace Assimp { /////////////////////////////////////////////////////////////////////////// // std::plus-family operates on operands with identical types - we need to @@ -231,7 +230,8 @@ private: // ---------------------------------------------------------------------------- /// This time binary arithmetic of v0 with a floating-point number - template