diff --git a/CHANGES.md b/CHANGES.md index e81e50f13..1834baa92 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,7 +14,11 @@ ##### Fixes :wrench: -- When a 3D Tiles Instanced 3D Mesh (i3dm) file contains an instance transform that cannot be decomposed into position, rotation, and scale, a warning will now be logged and an identity transformation will be used. Previously, an undefined transformation would be used. +- Fixed several problems with the loader for the 3D Tiles Instanced 3D Mesh (i3dm) format: + - When an instance transform cannot be decomposed into position, rotation, and scale, a warning will now be logged and an identity transformation will be used. Previously, an undefined transformation would be used. + - The `gltfUpAxis` property is now accounted for, if present. + - Paths to images in i3dm content are now resolved correctly. + - Extraneous spaces at the end of an external glTF URI are now ignored. These are sometimes added as padding in order to meet alignment requirements. - Removed an overly-eager degenerate triangle test in the 2D version of `IntersectionTests::pointInTriangle` that could discard intersections in small - but valid - triangles. - Fixed a bug while upsampling tiles for raster overlays that could cause them to have an incorrect bounding box, which in some cases would lead to the raster overlay being missing entirely from the upsampled tile. diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h index ed4459de4..6e95e98e2 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h @@ -21,7 +21,8 @@ struct BinaryToGltfConverter { private: static GltfConverterResult convertImmediate( const gsl::span& gltfBinary, - const CesiumGltfReader::GltfReaderOptions& options); + const CesiumGltfReader::GltfReaderOptions& options, + const AssetFetcher& assetFetcher); static CesiumGltfReader::GltfReader _gltfReader; }; } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h index f988b8535..bbfaef488 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -30,21 +31,24 @@ struct CESIUM3DTILESCONTENT_API AssetFetcher { const std::shared_ptr& pAssetAccessor_, const std::string& baseUrl_, const glm::dmat4 tileTransform_, - const std::vector& requestHeaders_) + const std::vector& requestHeaders_, + CesiumGeometry::Axis upAxis_) : asyncSystem(asyncSystem_), pAssetAccessor(pAssetAccessor_), baseUrl(baseUrl_), tileTransform(tileTransform_), - requestHeaders(requestHeaders_) {} + requestHeaders(requestHeaders_), + upAxis(upAxis_) {} CesiumAsync::Future get(const std::string& relativeUrl) const; - const CesiumAsync::AsyncSystem& asyncSystem; - const std::shared_ptr pAssetAccessor; - const std::string baseUrl; + CesiumAsync::AsyncSystem asyncSystem; + std::shared_ptr pAssetAccessor; + std::string baseUrl; glm::dmat4 tileTransform; // For ENU transforms in i3dm - const std::vector& requestHeaders; + std::vector requestHeaders; + CesiumGeometry::Axis upAxis; }; /** diff --git a/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp b/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp index 9a9bcf401..396f96df9 100644 --- a/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp @@ -6,10 +6,16 @@ CesiumGltfReader::GltfReader BinaryToGltfConverter::_gltfReader; GltfConverterResult BinaryToGltfConverter::convertImmediate( const gsl::span& gltfBinary, - const CesiumGltfReader::GltfReaderOptions& options) { + const CesiumGltfReader::GltfReaderOptions& options, + const AssetFetcher& assetFetcher) { CesiumGltfReader::GltfReaderResult loadedGltf = _gltfReader.readGltf(gltfBinary, options); + if (loadedGltf.model) { + loadedGltf.model->extras["gltfUpAxis"] = + static_cast>( + assetFetcher.upAxis); + } GltfConverterResult result; result.model = std::move(loadedGltf.model); result.errors.errors = std::move(loadedGltf.errors); @@ -22,6 +28,6 @@ CesiumAsync::Future BinaryToGltfConverter::convert( const CesiumGltfReader::GltfReaderOptions& options, const AssetFetcher& assetFetcher) { return assetFetcher.asyncSystem.createResolvedFuture( - convertImmediate(gltfBinary, options)); + convertImmediate(gltfBinary, options, assetFetcher)); } } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp index 5d803fc7f..a01a32a9a 100644 --- a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include @@ -420,8 +421,8 @@ CesiumAsync::Future convertI3dmContent( CesiumGeospatial::LocalHorizontalCoordinateSystem enu( (glm::dvec3(worldPos))); const glm::dmat4& ecef = enu.getLocalToEcefTransformation(); - // Express the rotation in the tile's coordinate system, like explicit - // I3dm instance rotations. + // Express the rotation in the tile's coordinate system, just like + // explicit i3dm instance rotations. glm::dmat4 tileFrame = worldTransformInv * ecef; return rotationFromUpRight( glm::vec3(tileFrame[1]), @@ -454,44 +455,81 @@ CesiumAsync::Future convertI3dmContent( }); } repositionInstances(decodedInstances); - AssetFetcherResult assetFetcherResult; + std::string baseUri; if (header.gltfFormat == 0) { - // Recursively fetch and read the glTF content. - auto gltfUri = std::string( - reinterpret_cast(gltfData.data()), - gltfData.size()); - return assetFetcher.get(gltfUri) - .thenImmediately( - [options, assetFetcher](AssetFetcherResult&& assetFetcherResult) - -> CesiumAsync::Future { - if (assetFetcherResult.errorList.hasErrors()) { - GltfConverterResult errorResult; - errorResult.errors.merge(assetFetcherResult.errorList); - return assetFetcher.asyncSystem.createResolvedFuture( - std::move(errorResult)); - } - return BinaryToGltfConverter::convert( - assetFetcherResult.bytes, - options, - assetFetcher); - }) - .thenImmediately([convertedI3dm = std::move(convertedI3dm)]( - GltfConverterResult&& converterResult) mutable { - if (converterResult.model) - convertedI3dm.gltfResult.model = std::move(converterResult.model); - convertedI3dm.gltfResult.errors.merge(converterResult.errors); - return convertedI3dm; + // The spec says that the URL can be padded with ' ' (0x20) characters in + // order to make the size of the whole i3dm file 8-byte aligned. + auto rLastNotSpace = + std::find_if_not(gltfData.rbegin(), gltfData.rend(), [](auto&& b) { + return b == std::byte(' '); }); + auto spaceDistance = static_cast(gltfData.rend() - rLastNotSpace); + std::string gltfUri( + reinterpret_cast(gltfData.data()), + spaceDistance); + baseUri = CesiumUtility::Uri::resolve(assetFetcher.baseUrl, gltfUri); } else { - return BinaryToGltfConverter::convert(gltfData, options, assetFetcher) - .thenImmediately([convertedI3dm = std::move(convertedI3dm)]( - GltfConverterResult&& converterResult) mutable { - if (converterResult.model) - convertedI3dm.gltfResult.model = std::move(converterResult.model); - convertedI3dm.gltfResult.errors.merge(converterResult.errors); - return convertedI3dm; - }); - } + baseUri = assetFetcher.baseUrl; + } + + auto getGltf = [&]() -> CesiumAsync::Future { + if (header.gltfFormat == 0) { + // Recursively fetch and read the glTF content. + return assetFetcher.get(baseUri).thenImmediately( + [options, assetFetcher](AssetFetcherResult&& assetFetcherResult) + -> CesiumAsync::Future { + if (assetFetcherResult.errorList.hasErrors()) { + GltfConverterResult errorResult; + errorResult.errors.merge(assetFetcherResult.errorList); + return assetFetcher.asyncSystem.createResolvedFuture( + std::move(errorResult)); + } + return BinaryToGltfConverter::convert( + assetFetcherResult.bytes, + options, + assetFetcher); + }); + } else { + return BinaryToGltfConverter::convert(gltfData, options, assetFetcher); + } + }; + + return getGltf() + .thenImmediately([options, assetFetcher, baseUri]( + GltfConverterResult&& converterResult) { + if (converterResult.model.has_value()) { + CesiumGltfReader::GltfReaderResult readerResult{ + std::move(*converterResult.model), + {}, + {}}; + CesiumAsync::HttpHeaders externalRequestHeaders( + assetFetcher.requestHeaders.begin(), + assetFetcher.requestHeaders.end()); + return CesiumGltfReader::GltfReader::resolveExternalData( + assetFetcher.asyncSystem, + baseUri, + externalRequestHeaders, + assetFetcher.pAssetAccessor, + options, + std::move(readerResult)); + } + return assetFetcher.asyncSystem.createResolvedFuture( + CesiumGltfReader::GltfReaderResult{ + std::nullopt, + std::move(converterResult.errors.errors), + {}}); + }) + .thenImmediately( + [convertedI3dm = std::move(convertedI3dm)]( + CesiumGltfReader::GltfReaderResult&& readerResult) mutable { + if (readerResult.model) + convertedI3dm.gltfResult.model = std::move(readerResult.model); + CesiumUtility::ErrorList resolvedExternalErrors{ + std::move(readerResult.errors), + {}}; + convertedI3dm.gltfResult.errors.merge(resolvedExternalErrors); + return convertedI3dm; + }); } glm::dmat4 diff --git a/Cesium3DTilesContent/test/ConvertTileToGltf.cpp b/Cesium3DTilesContent/test/ConvertTileToGltf.cpp index 792bbced8..c529ca3d0 100644 --- a/Cesium3DTilesContent/test/ConvertTileToGltf.cpp +++ b/Cesium3DTilesContent/test/ConvertTileToGltf.cpp @@ -20,7 +20,8 @@ AssetFetcher ConvertTileToGltf::makeAssetFetcher(const std::string& baseUrl) { fileAccessor, baseUrl, glm::dmat4(1.0), - requestHeaders); + requestHeaders, + CesiumGeometry::Axis::Y); } GltfConverterResult ConvertTileToGltf::fromB3dm( diff --git a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp index c9a31fcda..0d612a08b 100644 --- a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp @@ -173,7 +173,8 @@ CesiumAsync::Future requestTileContent( pAssetAccessor, tileUrl, tileTransform, - requestHeaders}; + requestHeaders, + CesiumGeometry::Axis::Y}; return converter(responseData, gltfOptions, assetFetcher) .thenImmediately([pLogger, tileUrl, pCompletedRequest, ellipsoid]( GltfConverterResult&& result) { diff --git a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp index 4f71c8a82..9cad4043d 100644 --- a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp @@ -185,7 +185,8 @@ CesiumAsync::Future requestTileContent( pAssetAccessor, tileUrl, tileTransform, - requestHeaders}; + requestHeaders, + CesiumGeometry::Axis::Y}; return converter(responseData, gltfOptions, assetFetcher) .thenImmediately([ellipsoid, pLogger, tileUrl, pCompletedRequest]( GltfConverterResult&& result) { diff --git a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp index 8c01b48f8..f557d79f8 100644 --- a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp +++ b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp @@ -939,7 +939,8 @@ TilesetJsonLoader::loadTileContent(const TileLoadInput& loadInput) { pAssetAccessor, tileUrl, tileTransform, - requestHeaders}; + requestHeaders, + upAxis}; CesiumGltfReader::GltfReaderOptions gltfOptions; gltfOptions.ktx2TranscodeTargets = contentOptions.ktx2TranscodeTargets;