diff --git a/Cesium3DTiles/include/Cesium3DTiles/RasterMappedTo3DTile.h b/Cesium3DTiles/include/Cesium3DTiles/RasterMappedTo3DTile.h index d932b4cbe..ce397dfb0 100644 --- a/Cesium3DTiles/include/Cesium3DTiles/RasterMappedTo3DTile.h +++ b/Cesium3DTiles/include/Cesium3DTiles/RasterMappedTo3DTile.h @@ -187,6 +187,7 @@ namespace Cesium3DTiles { glm::dvec2 _translation; glm::dvec2 _scale; AttachmentState _state; + bool _originalFailed; }; } diff --git a/Cesium3DTiles/include/Cesium3DTiles/TileID.h b/Cesium3DTiles/include/Cesium3DTiles/TileID.h index afcb23ccd..b7b7444b7 100644 --- a/Cesium3DTiles/include/Cesium3DTiles/TileID.h +++ b/Cesium3DTiles/include/Cesium3DTiles/TileID.h @@ -25,16 +25,15 @@ namespace Cesium3DTiles { * * A {@link CesiumGeometry::OctreeTileID}: This is an implicit * tile in the octree. The URL of the tile's content is formed * by instantiating the context's template URL with this ID. - * * A {@link CesiumGeometry::QuadtreeChild}: This tile doesn't + * * A {@link CesiumGeometry::UpsampledQuadtreeNode}: This tile doesn't * have any content, but content for it can be created by subdividing - * the parent tile's content into four equal tiles and taking the - * quadrant identified. + * the parent tile's content. */ typedef std::variant< std::string, CesiumGeometry::QuadtreeTileID, CesiumGeometry::OctreeTileID, - CesiumGeometry::QuadtreeChild + CesiumGeometry::UpsampledQuadtreeNode > TileID; } diff --git a/Cesium3DTiles/src/CompositeContent.cpp b/Cesium3DTiles/src/CompositeContent.cpp index e6c71defe..0d485ea5a 100644 --- a/Cesium3DTiles/src/CompositeContent.cpp +++ b/Cesium3DTiles/src/CompositeContent.cpp @@ -107,9 +107,21 @@ namespace Cesium3DTiles { } else if (innerTiles.size() == 1) { return std::move(innerTiles[0]); } else { - // TODO: combine all inner tiles into one glTF instead of return only the first. - SPDLOG_LOGGER_WARN(pLogger, "Composite tile contains multiple loadable inner tiles. Due to a temporary limitation, only the first will be used."); - return std::move(innerTiles[0]); + std::unique_ptr pResult = std::move(innerTiles[0]); + + for (size_t i = 1; i < innerTiles.size(); ++i) { + if (!innerTiles[i]->model) { + continue; + } + + if (pResult->model) { + pResult->model.value().merge(std::move(innerTiles[i]->model.value())); + } else { + pResult->model = std::move(innerTiles[i]->model); + } + } + + return pResult; } } diff --git a/Cesium3DTiles/src/GltfContent.cpp b/Cesium3DTiles/src/GltfContent.cpp index 8eed5c24b..240a0ba24 100644 --- a/Cesium3DTiles/src/GltfContent.cpp +++ b/Cesium3DTiles/src/GltfContent.cpp @@ -33,6 +33,10 @@ namespace Cesium3DTiles { pResult->model = std::move(loadedModel.model); + if (pResult->model) { + pResult->model.value().extras["Cesium3DTiles_TileUrl"] = url; + } + return pResult; } diff --git a/Cesium3DTiles/src/QuantizedMeshContent.cpp b/Cesium3DTiles/src/QuantizedMeshContent.cpp index 6280492c6..e8e83e172 100644 --- a/Cesium3DTiles/src/QuantizedMeshContent.cpp +++ b/Cesium3DTiles/src/QuantizedMeshContent.cpp @@ -948,6 +948,10 @@ namespace Cesium3DTiles { pResult->updatedBoundingVolume = BoundingRegion(rectangle, minimumHeight, maximumHeight); + if (pResult->model) { + pResult->model.value().extras["Cesium3DTiles_TileUrl"] = url; + } + return pResult; } diff --git a/Cesium3DTiles/src/RasterMappedTo3DTile.cpp b/Cesium3DTiles/src/RasterMappedTo3DTile.cpp index 54f6d2bb5..8bef69884 100644 --- a/Cesium3DTiles/src/RasterMappedTo3DTile.cpp +++ b/Cesium3DTiles/src/RasterMappedTo3DTile.cpp @@ -19,19 +19,24 @@ namespace Cesium3DTiles { _textureCoordinateRectangle(textureCoordinateRectangle), _translation(0.0, 0.0), _scale(1.0, 1.0), - _state(AttachmentState::Unattached) + _state(AttachmentState::Unattached), + _originalFailed(false) { } RasterMappedTo3DTile::MoreDetailAvailable RasterMappedTo3DTile::update(Tile& tile) { if (this->getState() == AttachmentState::Attached) { - return this->_pReadyTile && this->_pReadyTile->getID().level < this->_pReadyTile->getOverlay().getTileProvider()->getMaximumLevel() + return !this->_originalFailed && this->_pReadyTile && this->_pReadyTile->getID().level < this->_pReadyTile->getOverlay().getTileProvider()->getMaximumLevel() ? MoreDetailAvailable::Yes : MoreDetailAvailable::No; } // If the loading tile has failed, try its parent. while (this->_pLoadingTile && this->_pLoadingTile->getState() == RasterOverlayTile::LoadState::Failed && this->_pLoadingTile->getID().level > 0) { + // Note when our original tile fails to load so that we don't report more data available. + // This means - by design - we won't refine past a failed tile. + this->_originalFailed = true; + CesiumGeometry::QuadtreeTileID thisID = this->_pLoadingTile->getID(); CesiumGeometry::QuadtreeTileID parentID(thisID.level - 1, thisID.x >> 1, thisID.y >> 1); this->_pLoadingTile = this->_pLoadingTile->getOverlay().getTileProvider()->getTile(parentID); @@ -52,7 +57,7 @@ namespace Cesium3DTiles { this->_state = AttachmentState::Unattached; } - // Mark the loading tile read. + // Mark the loading tile ready. this->_pReadyTile = this->_pLoadingTile; this->_pLoadingTile = nullptr; @@ -119,7 +124,7 @@ namespace Cesium3DTiles { if (this->_pLoadingTile) { return MoreDetailAvailable::Unknown; } else { - return this->_pReadyTile && this->_pReadyTile->getID().level < this->_pReadyTile->getOverlay().getTileProvider()->getMaximumLevel() + return !this->_originalFailed && this->_pReadyTile && this->_pReadyTile->getID().level < this->_pReadyTile->getOverlay().getTileProvider()->getMaximumLevel() ? MoreDetailAvailable::Yes : MoreDetailAvailable::No; } diff --git a/Cesium3DTiles/src/Tile.cpp b/Cesium3DTiles/src/Tile.cpp index b17346fc7..16e371159 100644 --- a/Cesium3DTiles/src/Tile.cpp +++ b/Cesium3DTiles/src/Tile.cpp @@ -186,7 +186,7 @@ namespace Cesium3DTiles { if (!maybeRequestFuture) { // There is no content to load. But we may need to upsample. - const QuadtreeChild* pSubdivided = std::get_if(&this->getTileID()); + const UpsampledQuadtreeNode* pSubdivided = std::get_if(&this->getTileID()); if (pSubdivided) { // We can't upsample this tile until its parent tile is done loading. if (this->getParent() && this->getParent()->getState() == LoadState::Done) { @@ -316,7 +316,7 @@ namespace Cesium3DTiles { for (const Tile& child : this->getChildren()) { if ( child.getState() == Tile::LoadState::ContentLoading && - std::get_if(&child.getTileID()) != nullptr + std::get_if(&child.getTileID()) != nullptr ) { return false; } @@ -385,6 +385,32 @@ namespace Cesium3DTiles { return; } + // TODO: support upsampling non-quadtrees. + const QuadtreeTileID* pParentTileID = std::get_if(&parent.getTileID()); + if (!pParentTileID) { + const UpsampledQuadtreeNode* pUpsampledID = std::get_if(&parent.getTileID()); + if (pUpsampledID) { + pParentTileID = &pUpsampledID->tileID; + } + } + + if (!pParentTileID) { + return; + } + + // TODO: support upsampling non-implicit tiles. + if (!parent.getContext()->implicitContext) { + return; + } + + QuadtreeTileID swID(pParentTileID->level + 1, pParentTileID->x * 2, pParentTileID->y * 2); + QuadtreeTileID seID(swID.level, swID.x + 1, swID.y); + QuadtreeTileID nwID(swID.level, swID.x, swID.y + 1); + QuadtreeTileID neID(swID.level, swID.x + 1, swID.y + 1); + + QuadtreeTilingScheme& tilingScheme = parent.getContext()->implicitContext.value().tilingScheme; + Projection& projection = parent.getContext()->implicitContext.value().projection; + parent.createChildTiles(4); gsl::span children = parent.getChildren(); @@ -409,33 +435,31 @@ namespace Cesium3DTiles { nw.setParent(&parent); ne.setParent(&parent); - sw.setTileID(QuadtreeChild::LowerLeft); - se.setTileID(QuadtreeChild::LowerRight); - nw.setTileID(QuadtreeChild::UpperLeft); - ne.setTileID(QuadtreeChild::UpperRight); + sw.setTileID(UpsampledQuadtreeNode { swID }); + se.setTileID(UpsampledQuadtreeNode { seID }); + nw.setTileID(UpsampledQuadtreeNode { nwID }); + ne.setTileID(UpsampledQuadtreeNode { neID }); - const GlobeRectangle& rectangle = pRegion->getRectangle(); - CesiumGeospatial::Cartographic center = rectangle.computeCenter(); double minimumHeight = pRegion->getMinimumHeight(); double maximumHeight = pRegion->getMaximumHeight(); sw.setBoundingVolume(BoundingRegionWithLooseFittingHeights(BoundingRegion( - GlobeRectangle(rectangle.getWest(), rectangle.getSouth(), center.longitude, center.latitude), + unprojectRectangleSimple(projection, tilingScheme.tileToRectangle(swID)), minimumHeight, maximumHeight ))); se.setBoundingVolume(BoundingRegionWithLooseFittingHeights(BoundingRegion( - GlobeRectangle(center.longitude, rectangle.getSouth(), rectangle.getEast(), center.latitude), + unprojectRectangleSimple(projection, tilingScheme.tileToRectangle(seID)), minimumHeight, maximumHeight ))); nw.setBoundingVolume(BoundingRegionWithLooseFittingHeights(BoundingRegion( - GlobeRectangle(rectangle.getWest(), center.latitude, center.longitude, rectangle.getNorth()), + unprojectRectangleSimple(projection, tilingScheme.tileToRectangle(nwID)), minimumHeight, maximumHeight ))); ne.setBoundingVolume(BoundingRegionWithLooseFittingHeights(BoundingRegion( - GlobeRectangle(center.longitude, center.latitude, rectangle.getEast(), rectangle.getNorth()), + unprojectRectangleSimple(projection, tilingScheme.tileToRectangle(neID)), minimumHeight, maximumHeight ))); @@ -645,7 +669,7 @@ namespace Cesium3DTiles { void Tile::upsampleParent(std::vector&& projections) { Tile* pParent = this->getParent(); - const QuadtreeChild* pSubdividedParentID = std::get_if(&this->getTileID()); + const UpsampledQuadtreeNode* pSubdividedParentID = std::get_if(&this->getTileID()); assert(pParent != nullptr); assert(pParent->getState() == LoadState::Done); diff --git a/Cesium3DTiles/src/Tileset.cpp b/Cesium3DTiles/src/Tileset.cpp index fe35d5021..54488851e 100644 --- a/Cesium3DTiles/src/Tileset.cpp +++ b/Cesium3DTiles/src/Tileset.cpp @@ -1277,7 +1277,7 @@ namespace Cesium3DTiles { }); } - std::string operator()(QuadtreeChild /*subdividedParent*/) { + std::string operator()(UpsampledQuadtreeNode /*subdividedParent*/) { return std::string(); } }; diff --git a/Cesium3DTiles/src/upsampleGltfForRasterOverlays.cpp b/Cesium3DTiles/src/upsampleGltfForRasterOverlays.cpp index b3b584a68..d77e8c5b2 100644 --- a/Cesium3DTiles/src/upsampleGltfForRasterOverlays.cpp +++ b/Cesium3DTiles/src/upsampleGltfForRasterOverlays.cpp @@ -27,7 +27,7 @@ namespace Cesium3DTiles { Model& model, Mesh& mesh, MeshPrimitive& primitive, - CesiumGeometry::QuadtreeChild childID + CesiumGeometry::UpsampledQuadtreeNode childID ); struct FloatVertexAttribute { @@ -72,14 +72,22 @@ namespace Cesium3DTiles { static void addSkirts(std::vector& output, std::vector& indices, std::vector& attributes, - CesiumGeometry::QuadtreeChild childID, + CesiumGeometry::UpsampledQuadtreeNode childID, SkirtMeshMetadata ¤tSkirt, const SkirtMeshMetadata &parentSkirt, EdgeIndices &edgeIndices, int64_t vertexSizeFloats, int32_t positionAttributeIndex); - Model upsampleGltfForRasterOverlays(const Model& parentModel, CesiumGeometry::QuadtreeChild childID) { + static bool isWestChild(CesiumGeometry::UpsampledQuadtreeNode childID) { + return (childID.tileID.x % 2) == 0; + } + + static bool isSouthChild(CesiumGeometry::UpsampledQuadtreeNode childID) { + return (childID.tileID.y % 2) == 0; + } + + Model upsampleGltfForRasterOverlays(const Model& parentModel, CesiumGeometry::UpsampledQuadtreeNode childID) { Model result; // Copy the entire parent model except for the buffers, bufferViews, and accessors, which we'll be rewriting. @@ -283,7 +291,7 @@ namespace Cesium3DTiles { Model& model, Mesh& /*mesh*/, MeshPrimitive& primitive, - CesiumGeometry::QuadtreeChild childID + CesiumGeometry::UpsampledQuadtreeNode childID ) { // Add up the per-vertex size of all attributes and create buffers, bufferViews, and accessors std::vector attributes; @@ -392,8 +400,8 @@ namespace Cesium3DTiles { primitive.attributes.erase(attribute); } - bool keepAboveU = childID == CesiumGeometry::QuadtreeChild::LowerRight || childID == CesiumGeometry::QuadtreeChild::UpperRight; - bool keepAboveV = childID == CesiumGeometry::QuadtreeChild::UpperLeft || childID == CesiumGeometry::QuadtreeChild::UpperRight; + bool keepAboveU = !isWestChild(childID); + bool keepAboveV = !isSouthChild(childID); AccessorView uvView(parentModel, uvAccessorIndex); AccessorView indicesView(parentModel, primitive.indices); @@ -723,7 +731,7 @@ namespace Cesium3DTiles { static void addSkirts(std::vector& output, std::vector& indices, std::vector& attributes, - CesiumGeometry::QuadtreeChild childID, + CesiumGeometry::UpsampledQuadtreeNode childID, SkirtMeshMetadata ¤tSkirt, const SkirtMeshMetadata &parentSkirt, EdgeIndices &edgeIndices, @@ -736,7 +744,7 @@ namespace Cesium3DTiles { shortestSkirtHeight = glm::min(shortestSkirtHeight, parentSkirt.skirtNorthHeight); // west - if (childID == CesiumGeometry::QuadtreeChild::LowerLeft || childID == CesiumGeometry::QuadtreeChild::UpperLeft) { + if (isWestChild(childID)) { currentSkirt.skirtWestHeight = parentSkirt.skirtWestHeight; } else { @@ -763,7 +771,7 @@ namespace Cesium3DTiles { positionAttributeIndex); // south - if (childID == CesiumGeometry::QuadtreeChild::LowerLeft || childID == CesiumGeometry::QuadtreeChild::LowerRight) { + if (isSouthChild(childID)) { currentSkirt.skirtSouthHeight = parentSkirt.skirtSouthHeight; } else { @@ -790,7 +798,7 @@ namespace Cesium3DTiles { positionAttributeIndex); // east - if (childID == CesiumGeometry::QuadtreeChild::LowerRight || childID == CesiumGeometry::QuadtreeChild::UpperRight) { + if (!isWestChild(childID)) { currentSkirt.skirtEastHeight = parentSkirt.skirtEastHeight; } else { @@ -817,7 +825,7 @@ namespace Cesium3DTiles { positionAttributeIndex); // north - if (childID == CesiumGeometry::QuadtreeChild::UpperLeft || childID == CesiumGeometry::QuadtreeChild::UpperRight) { + if (!isSouthChild(childID)) { currentSkirt.skirtNorthHeight = parentSkirt.skirtNorthHeight; } else { @@ -849,7 +857,7 @@ namespace Cesium3DTiles { Model& model, Mesh& mesh, MeshPrimitive& primitive, - CesiumGeometry::QuadtreeChild childID + CesiumGeometry::UpsampledQuadtreeNode childID ) { if ( primitive.mode != MeshPrimitive::Mode::TRIANGLES || diff --git a/Cesium3DTiles/src/upsampleGltfForRasterOverlays.h b/Cesium3DTiles/src/upsampleGltfForRasterOverlays.h index 2ab75fbd8..32e19e512 100644 --- a/Cesium3DTiles/src/upsampleGltfForRasterOverlays.h +++ b/Cesium3DTiles/src/upsampleGltfForRasterOverlays.h @@ -5,6 +5,6 @@ namespace Cesium3DTiles { - CesiumGltf::Model upsampleGltfForRasterOverlays(const CesiumGltf::Model& parentModel, CesiumGeometry::QuadtreeChild childID); + CesiumGltf::Model upsampleGltfForRasterOverlays(const CesiumGltf::Model& parentModel, CesiumGeometry::UpsampledQuadtreeNode childID); } diff --git a/Cesium3DTiles/test/TestUpsampleGltfForRasterOverlay.cpp b/Cesium3DTiles/test/TestUpsampleGltfForRasterOverlay.cpp index 3741aed14..015103852 100644 --- a/Cesium3DTiles/test/TestUpsampleGltfForRasterOverlay.cpp +++ b/Cesium3DTiles/test/TestUpsampleGltfForRasterOverlay.cpp @@ -134,8 +134,13 @@ TEST_CASE("Test upsample tile without skirts") { center.x, center.z, -center.y, 1.0 }; + CesiumGeometry::UpsampledQuadtreeNode lowerLeft { CesiumGeometry::QuadtreeTileID(1, 0, 0) }; + CesiumGeometry::UpsampledQuadtreeNode upperLeft { CesiumGeometry::QuadtreeTileID(1, 0, 1) }; + CesiumGeometry::UpsampledQuadtreeNode lowerRight { CesiumGeometry::QuadtreeTileID(1, 1, 0) }; + CesiumGeometry::UpsampledQuadtreeNode upperRight { CesiumGeometry::QuadtreeTileID(1, 1, 1) }; + SECTION("Upsample bottom left child") { - Model upsampledModel = upsampleGltfForRasterOverlays(model, CesiumGeometry::QuadtreeChild::LowerLeft); + Model upsampledModel = upsampleGltfForRasterOverlays(model, lowerLeft); REQUIRE(upsampledModel.meshes.size() == 1); const Mesh& upsampledMesh = upsampledModel.meshes.back(); @@ -171,7 +176,7 @@ TEST_CASE("Test upsample tile without skirts") { } SECTION("Upsample upper left child") { - Model upsampledModel = upsampleGltfForRasterOverlays(model, CesiumGeometry::QuadtreeChild::UpperLeft); + Model upsampledModel = upsampleGltfForRasterOverlays(model, upperLeft); REQUIRE(upsampledModel.meshes.size() == 1); const Mesh& upsampledMesh = upsampledModel.meshes.back(); @@ -207,7 +212,7 @@ TEST_CASE("Test upsample tile without skirts") { } SECTION("Upsample upper right child") { - Model upsampledModel = upsampleGltfForRasterOverlays(model, CesiumGeometry::QuadtreeChild::UpperRight); + Model upsampledModel = upsampleGltfForRasterOverlays(model, upperRight); REQUIRE(upsampledModel.meshes.size() == 1); const Mesh& upsampledMesh = upsampledModel.meshes.back(); @@ -243,7 +248,7 @@ TEST_CASE("Test upsample tile without skirts") { } SECTION("Upsample bottom right child") { - Model upsampledModel = upsampleGltfForRasterOverlays(model, CesiumGeometry::QuadtreeChild::LowerRight); + Model upsampledModel = upsampleGltfForRasterOverlays(model, lowerRight); REQUIRE(upsampledModel.meshes.size() == 1); const Mesh& upsampledMesh = upsampledModel.meshes.back(); @@ -293,7 +298,7 @@ TEST_CASE("Test upsample tile without skirts") { primitive.extras = SkirtMeshMetadata::createGltfExtras(skirtMeshMetadata); SECTION("Check bottom left skirt") { - Model upsampledModel = upsampleGltfForRasterOverlays(model, CesiumGeometry::QuadtreeChild::LowerLeft); + Model upsampledModel = upsampleGltfForRasterOverlays(model, lowerLeft); REQUIRE(upsampledModel.meshes.size() == 1); const Mesh& upsampledMesh = upsampledModel.meshes.back(); @@ -328,7 +333,7 @@ TEST_CASE("Test upsample tile without skirts") { } SECTION("Check upper left skirt") { - Model upsampledModel = upsampleGltfForRasterOverlays(model, CesiumGeometry::QuadtreeChild::UpperLeft); + Model upsampledModel = upsampleGltfForRasterOverlays(model, upperLeft); REQUIRE(upsampledModel.meshes.size() == 1); const Mesh& upsampledMesh = upsampledModel.meshes.back(); @@ -367,7 +372,7 @@ TEST_CASE("Test upsample tile without skirts") { } SECTION("Check upper right skirt") { - Model upsampledModel = upsampleGltfForRasterOverlays(model, CesiumGeometry::QuadtreeChild::UpperRight); + Model upsampledModel = upsampleGltfForRasterOverlays(model, upperRight); REQUIRE(upsampledModel.meshes.size() == 1); const Mesh& upsampledMesh = upsampledModel.meshes.back(); @@ -402,7 +407,7 @@ TEST_CASE("Test upsample tile without skirts") { } SECTION("Check bottom right skirt") { - Model upsampledModel = upsampleGltfForRasterOverlays(model, CesiumGeometry::QuadtreeChild::LowerRight); + Model upsampledModel = upsampleGltfForRasterOverlays(model, lowerRight); REQUIRE(upsampledModel.meshes.size() == 1); const Mesh& upsampledMesh = upsampledModel.meshes.back(); diff --git a/CesiumGeometry/include/CesiumGeometry/QuadtreeTileID.h b/CesiumGeometry/include/CesiumGeometry/QuadtreeTileID.h index 21f2aafa4..5547d099c 100644 --- a/CesiumGeometry/include/CesiumGeometry/QuadtreeTileID.h +++ b/CesiumGeometry/include/CesiumGeometry/QuadtreeTileID.h @@ -5,32 +5,6 @@ namespace CesiumGeometry { class QuadtreeTilingScheme; - /** - * @brief Identifies one of the four children of a quadtree node. - */ - enum class QuadtreeChild { - - /** - * @brief The lower left child node. - */ - LowerLeft = 0, - - /** - * @brief The lower right child node. - */ - LowerRight = 1, - - /** - * @brief The upper left child node. - */ - UpperLeft = 2, - - /** - * @brief The upper right child node. - */ - UpperRight = 3 - }; - /** * @brief Uniquely identifies a node in a quadtree. * @@ -98,6 +72,9 @@ namespace CesiumGeometry { uint32_t y; }; + struct CESIUMGEOMETRY_API UpsampledQuadtreeNode final { + QuadtreeTileID tileID; + }; } namespace std { diff --git a/CesiumGltf/include/CesiumGltf/AccessorView.h b/CesiumGltf/include/CesiumGltf/AccessorView.h index 1efbd6c59..229da9a8a 100644 --- a/CesiumGltf/include/CesiumGltf/AccessorView.h +++ b/CesiumGltf/include/CesiumGltf/AccessorView.h @@ -122,7 +122,6 @@ namespace CesiumGltf { this->create(model, accessor); } - /** * @brief Creates a new instance from a given model and accessor index. * @@ -228,4 +227,127 @@ namespace CesiumGltf { } }; + /** + * @brief Contains types that may optionally be used with {@link AccessorView} for various {@link Accessor::componentType} values. + */ + struct AccessorTypes { + #pragma pack(push, 1) + + template + struct SCALAR { + T value[1]; + }; + + template + struct VEC2 { + T value[2]; + }; + + template + struct VEC3 { + T value[3]; + }; + + template + struct VEC4 { + T value[4]; + }; + + template + struct MAT2 { + T value[4]; + }; + + template + struct MAT3 { + T value[9]; + }; + + template + struct MAT4 { + T value[16]; + }; + + #pragma pack(pop) + }; + + namespace Impl { + template + std::invoke_result_t> createAccessorView(const Model& model, const Accessor& accessor, TCallback&& callback) { + switch (accessor.type) { + case Accessor::Type::SCALAR: + return callback(AccessorView>(model, accessor)); + case Accessor::Type::VEC2: + return callback(AccessorView>(model, accessor)); + case Accessor::Type::VEC3: + return callback(AccessorView>(model, accessor)); + case Accessor::Type::VEC4: + return callback(AccessorView>(model, accessor)); + case Accessor::Type::MAT2: + return callback(AccessorView>(model, accessor)); + case Accessor::Type::MAT3: + return callback(AccessorView>(model, accessor)); + case Accessor::Type::MAT4: + return callback(AccessorView>(model, accessor)); + default: + return callback(AccessorView()); + } + } + } + + /** + * @brief Creates an appropriate {@link AccessorView} for a given accessor. + * + * The created accessor is provided via a callback, which is a function that can be + * invoked with all possible {@link AccessorView} types. If an accessor cannot be + * created, the callback will be invoked with `AccessorView`. + * + * @tparam TCallback The callback. + * @param model The model to access. + * @param accessor The accessor to view. + * @param callback The callback that receives the created accessor. + * @return The value returned by the callback. + */ + template + std::invoke_result_t> createAccessorView(const Model& model, const Accessor& accessor, TCallback&& callback) { + switch (accessor.componentType) { + case Accessor::ComponentType::BYTE: + return ::CesiumGltf::Impl::createAccessorView(model, accessor, std::forward(callback)); + case Accessor::ComponentType::UNSIGNED_BYTE: + return ::CesiumGltf::Impl::createAccessorView(model, accessor, std::forward(callback)); + case Accessor::ComponentType::SHORT: + return ::CesiumGltf::Impl::createAccessorView(model, accessor, std::forward(callback)); + case Accessor::ComponentType::UNSIGNED_SHORT: + return ::CesiumGltf::Impl::createAccessorView(model, accessor, std::forward(callback)); + case Accessor::ComponentType::UNSIGNED_INT: + return ::CesiumGltf::Impl::createAccessorView(model, accessor, std::forward(callback)); + case Accessor::ComponentType::FLOAT: + return ::CesiumGltf::Impl::createAccessorView(model, accessor, std::forward(callback)); + default: + return callback(AccessorView()); + } + } + + /** + * @brief Creates an appropriate {@link AccessorView} for a given accessor. + * + * The created accessor is provided via a callback, which is a function that can be + * invoked with all possible {@link AccessorView} types. If an accessor cannot be + * created, the callback will be invoked with `AccessorView`. + * + * @tparam TCallback The callback. + * @param model The model to access. + * @param accessorIndex The index of the accessor to view in {@link Model::accessors}. + * @param callback The callback that receives the created accessor. + * @return The value returned by the callback. + */ + template + std::invoke_result_t> createAccessorView(const Model& model, int32_t accessorIndex, TCallback&& callback) { + const Accessor* pAccessor = Model::getSafe(&model.accessors, accessorIndex); + if (!pAccessor) { + return callback(AccessorView()); + } + + return createAccessorView(model, *pAccessor, callback); + } } diff --git a/CesiumGltfReader/src/decodeDraco.cpp b/CesiumGltfReader/src/decodeDraco.cpp index b5dda07e7..2e953a78e 100644 --- a/CesiumGltfReader/src/decodeDraco.cpp +++ b/CesiumGltfReader/src/decodeDraco.cpp @@ -96,6 +96,20 @@ namespace { pIndicesAccessor->count = pMesh->num_faces() * 3; } + draco::PointIndex::ValueType numPoint = pMesh->num_points(); + Accessor::ComponentType supposedComponentType = Accessor::ComponentType::UNSIGNED_BYTE; + if (numPoint < static_cast(std::numeric_limits::max())) { + supposedComponentType = Accessor::ComponentType::UNSIGNED_BYTE; + } else if (numPoint < static_cast(std::numeric_limits::max())) { + supposedComponentType = Accessor::ComponentType::UNSIGNED_SHORT; + } else { + supposedComponentType = Accessor::ComponentType::UNSIGNED_INT; + } + + if (static_cast(supposedComponentType) > static_cast(pIndicesAccessor->componentType)) { + pIndicesAccessor->componentType = supposedComponentType; + } + pIndicesAccessor->bufferView = static_cast(model.bufferViews.size()); BufferView& indicesBufferView = model.bufferViews.emplace_back();