Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Full support for multiple-b3dm-in-cmpt tiles, and some other fixes #109

Merged
merged 24 commits into from
Feb 2, 2021
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
133b72c
Merge remote-tracking branch 'origin/composite' into cmpt
kring Jan 21, 2021
5c0c0f0
Merge remote-tracking branch 'origin/master' into cmpt
kring Jan 21, 2021
7a2b097
Support multiple b3dms in a cmpt.
kring Jan 21, 2021
dff2d63
Add name to glTF model to aid debugging.
kring Jan 21, 2021
06fe04d
Preserve precision in upsampled tile rectangles.
kring Jan 22, 2021
d4cea4f
Final, doc, examples.
kring Jan 25, 2021
6900396
Merge remote-tracking branch 'origin/gltf' into cmpt
kring Jan 25, 2021
b3e4a85
Merge remote-tracking branch 'origin/gltf' into cmpt
kring Jan 25, 2021
4eff250
Fix crash caused by runaway refinement.
kring Jan 25, 2021
f6c960f
Merge remote-tracking branch 'origin/gltf' into cmpt
kring Jan 25, 2021
05d6262
Add AccessorVisitor.
kring Jan 25, 2021
358e2a5
Remove AccessorView, replace with an AccessorView helper.
kring Jan 25, 2021
549ed29
upgrade draco index
baothientran Jan 25, 2021
19bec58
Merge branch 'cmpt' into draco-index-upgrade
baothientran Jan 25, 2021
b8a2234
only use unsigned type for draco indices
baothientran Jan 26, 2021
1087f4f
initialize draco accessor index to be unsigned byte
baothientran Jan 26, 2021
fab6781
use unsigned int if num of draco vertices over max uint16_t
baothientran Jan 26, 2021
52b510b
Merge pull request #110 from CesiumGS/draco-index-upgrade
kring Jan 27, 2021
d61295c
Merge remote-tracking branch 'origin/gltf' into cmpt
kring Jan 27, 2021
53804f9
Merge remote-tracking branch 'origin/gltf' into cmpt
kring Jan 27, 2021
8bc5d73
Fix gcc/clang build errors.
kring Jan 29, 2021
5a37470
Merge remote-tracking branch 'origin/gltf' into cmpt
kring Jan 29, 2021
79763f1
Merge remote-tracking branch 'origin/master' into cmpt
kring Feb 2, 2021
13ce898
Fix copy/paste fail.
kring Feb 2, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cesium3DTiles/include/Cesium3DTiles/RasterMappedTo3DTile.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ namespace Cesium3DTiles {
glm::dvec2 _translation;
glm::dvec2 _scale;
AttachmentState _state;
bool _originalFailed;
};

}
7 changes: 3 additions & 4 deletions Cesium3DTiles/include/Cesium3DTiles/TileID.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

}
128 changes: 128 additions & 0 deletions Cesium3DTiles/src/CompositeContent.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#include "CompositeContent.h"
#include "Cesium3DTiles/GltfContent.h"
#include "Cesium3DTiles/spdlog-cesium.h"
#include "Cesium3DTiles/TileContentFactory.h"
#include <rapidjson/document.h>
#include <stdexcept>

namespace {
#pragma pack(push, 1)
struct CmptHeader {
char magic[4];
uint32_t version;
uint32_t byteLength;
uint32_t tilesLength;
};

struct InnerHeader {
char magic[4];
uint32_t version;
uint32_t byteLength;
};
#pragma pack(pop)

static_assert(sizeof(CmptHeader) == 16);
static_assert(sizeof(InnerHeader) == 12);
}

namespace Cesium3DTiles {

std::unique_ptr<TileContentLoadResult> CompositeContent::load(
std::shared_ptr<spdlog::logger> pLogger,
const TileContext& context,
const TileID& tileID,
const BoundingVolume& tileBoundingVolume,
double tileGeometricError,
const glm::dmat4& tileTransform,
const std::optional<BoundingVolume>& tileContentBoundingVolume,
TileRefine tileRefine,
const std::string& url,
const gsl::span<const uint8_t>& data
) {
if (data.size() < sizeof(CmptHeader)) {
SPDLOG_LOGGER_WARN(pLogger, "Composite tile {} must be at least 16 bytes.", url);
return nullptr;
}

const CmptHeader* pHeader = reinterpret_cast<const CmptHeader*>(data.data());
if (std::string(pHeader->magic, 4) != "cmpt") {
SPDLOG_LOGGER_WARN(pLogger, "Composite tile does not have the expected magic vaue 'cmpt'.");
return nullptr;
}

if (pHeader->version != 1) {
SPDLOG_LOGGER_WARN(pLogger, "Unsupported composite tile version {}.", pHeader->version);
return nullptr;
}

if (pHeader->byteLength > data.size()) {
SPDLOG_LOGGER_WARN(pLogger, "Composite tile byteLength is {} but only {} bytes are available.", pHeader->byteLength, data.size());
return nullptr;
}

std::vector<std::unique_ptr<TileContentLoadResult>> innerTiles;

uint32_t pos = sizeof(CmptHeader);

for (uint32_t i = 0; i < pHeader->tilesLength; ++i) {
if (pos + sizeof(InnerHeader) > pHeader->byteLength) {
SPDLOG_LOGGER_WARN(pLogger, "Composite tile ends before all embedded tiles could be read.");
break;
}

const InnerHeader* pInner = reinterpret_cast<const InnerHeader*>(data.data() + pos);
if (pos + pInner->byteLength > pHeader->byteLength) {
SPDLOG_LOGGER_WARN(pLogger, "Composite tile ends before all embedded tiles could be read.");
break;
}

gsl::span<const uint8_t> innerData(data.data() + pos, pInner->byteLength);

std::unique_ptr<TileContentLoadResult> pInnerLoadResult = TileContentFactory::createContent(
pLogger,
context,
tileID,
tileBoundingVolume,
tileGeometricError,
tileTransform,
tileContentBoundingVolume,
tileRefine,
url,
"",
innerData
);

if (pInnerLoadResult) {
innerTiles.emplace_back(std::move(pInnerLoadResult));
}

pos += pInner->byteLength;
}

if (innerTiles.size() == 0) {
if (pHeader->tilesLength > 0) {
SPDLOG_LOGGER_WARN(pLogger, "Composite tile does not contain any loadable inner tiles.");
}
return nullptr;
} else if (innerTiles.size() == 1) {
return std::move(innerTiles[0]);
} else {
std::unique_ptr<TileContentLoadResult> 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;
}
}

}
36 changes: 36 additions & 0 deletions Cesium3DTiles/src/CompositeContent.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#pragma once

#include "Cesium3DTiles/BoundingVolume.h"
#include "Cesium3DTiles/Library.h"
#include "Cesium3DTiles/TileID.h"
#include "Cesium3DTiles/TileRefine.h"
#include "Cesium3DTiles/TileContentLoadResult.h"
#include <memory>
#include <string>
#include <spdlog/fwd.h>

namespace Cesium3DTiles {

class Tileset;

/**
* @brief Creates a {@link TileContentLoadResult} from CMPT data.
*/
class CESIUM3DTILES_API CompositeContent final {
public:
/** @copydoc ExternalTilesetContent::load */
static std::unique_ptr<TileContentLoadResult> load(
std::shared_ptr<spdlog::logger> pLogger,
const TileContext& context,
const TileID& tileID,
const BoundingVolume& tileBoundingVolume,
double tileGeometricError,
const glm::dmat4& tileTransform,
const std::optional<BoundingVolume>& tileContentBoundingVolume,
TileRefine tileRefine,
const std::string& url,
const gsl::span<const uint8_t>& data
);
};

}
18 changes: 11 additions & 7 deletions Cesium3DTiles/src/ExternalTilesetContent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ namespace Cesium3DTiles {
const gsl::span<const uint8_t>& data
) {
std::unique_ptr<TileContentLoadResult> pResult = std::make_unique<TileContentLoadResult>();

rapidjson::Document tilesetJson;
tilesetJson.Parse(reinterpret_cast<const char*>(data.data()), data.size());

if (tilesetJson.HasParseError()) {
SPDLOG_LOGGER_ERROR(pLogger, "Error when parsing tileset JSON, error code {} at byte offset {}", tilesetJson.GetParseError(), tilesetJson.GetErrorOffset());
return pResult;
}

pResult->childTiles.emplace(1);

pResult->pNewTileContext = std::make_unique<TileContext>();
Expand All @@ -30,14 +39,9 @@ namespace Cesium3DTiles {
pContext->version = context.version;
pContext->failedTileCallback = context.failedTileCallback;

rapidjson::Document tilesetJson;
tilesetJson.Parse(reinterpret_cast<const char*>(data.data()), data.size());
pResult->childTiles.value()[0].setContext(pContext);

if (tilesetJson.HasParseError()) {
SPDLOG_LOGGER_ERROR(pLogger, "Error when parsing tileset JSON, error code {} at byte offset {}", tilesetJson.GetParseError(), tilesetJson.GetErrorOffset());
} else {
context.pTileset->loadTilesFromJson(pResult->childTiles.value()[0], tilesetJson, tileTransform, tileRefine, *pContext, pLogger);
}
context.pTileset->loadTilesFromJson(pResult->childTiles.value()[0], tilesetJson, tileTransform, tileRefine, *pContext, pLogger);

return pResult;
}
Expand Down
4 changes: 4 additions & 0 deletions Cesium3DTiles/src/GltfContent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ namespace Cesium3DTiles {

pResult->model = std::move(loadedModel.model);

if (pResult->model) {
pResult->model.value().extras["Cesium3DTiles_TileUrl"] = url;
}

return pResult;
}

Expand Down
4 changes: 4 additions & 0 deletions Cesium3DTiles/src/QuantizedMeshContent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -948,6 +948,10 @@ namespace Cesium3DTiles {

pResult->updatedBoundingVolume = BoundingRegion(rectangle, minimumHeight, maximumHeight);

if (pResult->model) {
pResult->model.value().extras["Cesium3DTiles_TileUrl"] = url;
}

return pResult;
}

Expand Down
13 changes: 9 additions & 4 deletions Cesium3DTiles/src/RasterMappedTo3DTile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;

Expand Down Expand Up @@ -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;
}
Expand Down
50 changes: 37 additions & 13 deletions Cesium3DTiles/src/Tile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<QuadtreeChild>(&this->getTileID());
const UpsampledQuadtreeNode* pSubdivided = std::get_if<UpsampledQuadtreeNode>(&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) {
Expand Down Expand Up @@ -316,7 +316,7 @@ namespace Cesium3DTiles {
for (const Tile& child : this->getChildren()) {
if (
child.getState() == Tile::LoadState::ContentLoading &&
std::get_if<CesiumGeometry::QuadtreeChild>(&child.getTileID()) != nullptr
std::get_if<CesiumGeometry::UpsampledQuadtreeNode>(&child.getTileID()) != nullptr
) {
return false;
}
Expand Down Expand Up @@ -385,6 +385,32 @@ namespace Cesium3DTiles {
return;
}

// TODO: support upsampling non-quadtrees.
const QuadtreeTileID* pParentTileID = std::get_if<QuadtreeTileID>(&parent.getTileID());
if (!pParentTileID) {
const UpsampledQuadtreeNode* pUpsampledID = std::get_if<UpsampledQuadtreeNode>(&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<Tile> children = parent.getChildren();
Expand All @@ -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
)));
Expand Down Expand Up @@ -645,7 +669,7 @@ namespace Cesium3DTiles {

void Tile::upsampleParent(std::vector<CesiumGeospatial::Projection>&& projections) {
Tile* pParent = this->getParent();
const QuadtreeChild* pSubdividedParentID = std::get_if<QuadtreeChild>(&this->getTileID());
const UpsampledQuadtreeNode* pSubdividedParentID = std::get_if<UpsampledQuadtreeNode>(&this->getTileID());

assert(pParent != nullptr);
assert(pParent->getState() == LoadState::Done);
Expand Down
Loading