Skip to content

Commit

Permalink
Merge branch 'main' into correct-variable-length-arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
j9liu committed Jan 23, 2024
2 parents 5d288e8 + 65aaa77 commit a56def2
Show file tree
Hide file tree
Showing 19 changed files with 765 additions and 72 deletions.
10 changes: 10 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,17 @@

##### Additions :tada:

- Added support for Web Map Tile Service (WMTS) with `WebMapTileServiceRasterOverlay`.
- Added conversions from `std::string` to other metadata types in `MetadataConversions`. This enables the same conversions as `std::string_view`, while allowing runtime engines to use `std::string` for convenience.
- Added `applyTextureTransform` property to `TilesetOptions`.

##### Fixes :wrench:

- Fixed `FeatureIdTextureView` ignoring the wrap values specified on the texture's sampler.

##### Fixes :wrench:

- Fixed a bug that could cause binary implicit tiling subtrees with buffers padded to 8-bytes to fail to load.

##### Fixes :wrench:

Expand Down
29 changes: 15 additions & 14 deletions Cesium3DTilesContent/src/SubtreeAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,21 +152,22 @@ SubtreeAvailability::loadSubtree(
[pLogger, subtreeUrl, subdivisionScheme, levelsInSubtree, pReader](
ReadJsonResult<Subtree>&& subtree)
-> std::optional<SubtreeAvailability> {
if (!subtree.errors.empty()) {
SPDLOG_LOGGER_ERROR(
pLogger,
"Errors while loading subtree from {}:\n- {}",
subtreeUrl,
CesiumUtility::joinToString(subtree.errors, "\n- "));
}
if (!subtree.warnings.empty()) {
SPDLOG_LOGGER_WARN(
pLogger,
"Warnings while loading subtree from {}:\n- {}",
subtreeUrl,
CesiumUtility::joinToString(subtree.warnings, "\n- "));
}

if (!subtree.value) {
if (!subtree.errors.empty()) {
SPDLOG_LOGGER_ERROR(
pLogger,
"Errors while loading subtree from {}:\n- {}",
subtreeUrl,
CesiumUtility::joinToString(subtree.errors, "\n- "));
}
if (!subtree.warnings.empty()) {
SPDLOG_LOGGER_WARN(
pLogger,
"Warnings while loading subtree from {}:\n- {}",
subtreeUrl,
CesiumUtility::joinToString(subtree.warnings, "\n- "));
}
return std::nullopt;
}

Expand Down
10 changes: 9 additions & 1 deletion Cesium3DTilesReader/src/SubtreeFileReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,16 @@ Future<ReadJsonResult<Subtree>> SubtreeFileReader::loadBinary(
}

const int64_t binaryChunkSize = static_cast<int64_t>(binaryChunk.size());

// We allow - but don't require - 8-byte padding.
int64_t maxPaddingBytes = 0;
int64_t paddingRemainder = buffer.byteLength % 8;
if (paddingRemainder > 0) {
maxPaddingBytes = 8 - paddingRemainder;
}

if (buffer.byteLength > binaryChunkSize ||
buffer.byteLength + 3 < binaryChunkSize) {
buffer.byteLength + maxPaddingBytes < binaryChunkSize) {
result.errors.emplace_back("Subtree binary chunk size does not match the "
"size of the first buffer in the JSON chunk.");
return asyncSystem.createResolvedFuture(std::move(result));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ struct CESIUM3DTILESSELECTION_API TilesetContentOptions {
* the ideal target gpu-compressed pixel format to transcode to.
*/
CesiumGltf::Ktx2TranscodeTargets ktx2TranscodeTargets;

/**
* @brief Whether or not to transform texture coordinates during load when
* textures have the `KHR_texture_transform` extension. Set this to false if
* texture coordinates will be transformed another way, such as in a vertex
* shader.
*/
bool applyTextureTransform = true;
};

/**
Expand Down
20 changes: 14 additions & 6 deletions Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,12 @@ CesiumAsync::Future<TileLoadResult> requestTileContent(
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::string& tileUrl,
const std::vector<CesiumAsync::IAssetAccessor::THeader>& requestHeaders,
CesiumGltf::Ktx2TranscodeTargets ktx2TranscodeTargets) {
CesiumGltf::Ktx2TranscodeTargets ktx2TranscodeTargets,
bool applyTextureTransform) {
return pAssetAccessor->get(asyncSystem, tileUrl, requestHeaders)
.thenInWorkerThread([pLogger, ktx2TranscodeTargets](
.thenInWorkerThread([pLogger,
ktx2TranscodeTargets,
applyTextureTransform](
std::shared_ptr<CesiumAsync::IAssetRequest>&&
pCompletedRequest) mutable {
const CesiumAsync::IAssetResponse* pResponse =
Expand Down Expand Up @@ -143,6 +146,7 @@ CesiumAsync::Future<TileLoadResult> requestTileContent(
// Convert to gltf
CesiumGltfReader::GltfReaderOptions gltfOptions;
gltfOptions.ktx2TranscodeTargets = ktx2TranscodeTargets;
gltfOptions.applyTextureTransform = applyTextureTransform;
GltfConverterResult result = converter(responseData, gltfOptions);

// Report any errors if there are any
Expand Down Expand Up @@ -221,10 +225,13 @@ ImplicitOctreeLoader::loadTileContent(const TileLoadInput& loadInput) {
this->addSubtreeAvailability(
subtreeID,
std::move(*subtreeAvailability));
}

// tell client to retry later
return TileLoadResult::createRetryLaterResult(nullptr);
// tell client to retry later
return TileLoadResult::createRetryLaterResult(nullptr);
} else {
// Subtree load failed, so this tile fails, too.
return TileLoadResult::createFailedResult(nullptr);
}
});
}

Expand Down Expand Up @@ -253,7 +260,8 @@ ImplicitOctreeLoader::loadTileContent(const TileLoadInput& loadInput) {
pAssetAccessor,
tileUrl,
requestHeaders,
contentOptions.ktx2TranscodeTargets);
contentOptions.ktx2TranscodeTargets,
contentOptions.applyTextureTransform);
}

TileChildrenResult ImplicitOctreeLoader::createTileChildren(const Tile& tile) {
Expand Down
20 changes: 14 additions & 6 deletions Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,12 @@ CesiumAsync::Future<TileLoadResult> requestTileContent(
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::string& tileUrl,
const std::vector<CesiumAsync::IAssetAccessor::THeader>& requestHeaders,
CesiumGltf::Ktx2TranscodeTargets ktx2TranscodeTargets) {
CesiumGltf::Ktx2TranscodeTargets ktx2TranscodeTargets,
bool applyTextureTransform) {
return pAssetAccessor->get(asyncSystem, tileUrl, requestHeaders)
.thenInWorkerThread([pLogger, ktx2TranscodeTargets](
.thenInWorkerThread([pLogger,
ktx2TranscodeTargets,
applyTextureTransform](
std::shared_ptr<CesiumAsync::IAssetRequest>&&
pCompletedRequest) mutable {
const CesiumAsync::IAssetResponse* pResponse =
Expand Down Expand Up @@ -153,6 +156,7 @@ CesiumAsync::Future<TileLoadResult> requestTileContent(
// Convert to gltf
CesiumGltfReader::GltfReaderOptions gltfOptions;
gltfOptions.ktx2TranscodeTargets = ktx2TranscodeTargets;
gltfOptions.applyTextureTransform = applyTextureTransform;
GltfConverterResult result = converter(responseData, gltfOptions);

// Report any errors if there are any
Expand Down Expand Up @@ -260,10 +264,13 @@ ImplicitQuadtreeLoader::loadTileContent(const TileLoadInput& loadInput) {
this->addSubtreeAvailability(
subtreeID,
std::move(*subtreeAvailability));
}

// tell client to retry later
return TileLoadResult::createRetryLaterResult(nullptr);
// tell client to retry later
return TileLoadResult::createRetryLaterResult(nullptr);
} else {
// Subtree load failed, so this tile fails, too.
return TileLoadResult::createFailedResult(nullptr);
}
});
}

Expand Down Expand Up @@ -292,7 +299,8 @@ ImplicitQuadtreeLoader::loadTileContent(const TileLoadInput& loadInput) {
pAssetAccessor,
tileUrl,
requestHeaders,
contentOptions.ktx2TranscodeTargets);
contentOptions.ktx2TranscodeTargets,
contentOptions.applyTextureTransform);
}

TileChildrenResult
Expand Down
2 changes: 2 additions & 0 deletions Cesium3DTilesSelection/src/TilesetContentManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,8 @@ postProcessContentInWorkerThread(
CesiumGltfReader::GltfReaderOptions gltfOptions;
gltfOptions.ktx2TranscodeTargets =
tileLoadInfo.contentOptions.ktx2TranscodeTargets;
gltfOptions.applyTextureTransform =
tileLoadInfo.contentOptions.applyTextureTransform;

auto asyncSystem = tileLoadInfo.asyncSystem;
auto pAssetAccessor = tileLoadInfo.pAssetAccessor;
Expand Down
2 changes: 2 additions & 0 deletions Cesium3DTilesSelection/src/TilesetJsonLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,8 @@ TilesetJsonLoader::loadTileContent(const TileLoadInput& loadInput) {
CesiumGltfReader::GltfReaderOptions gltfOptions;
gltfOptions.ktx2TranscodeTargets =
contentOptions.ktx2TranscodeTargets;
gltfOptions.applyTextureTransform =
contentOptions.applyTextureTransform;
GltfConverterResult result = converter(responseData, gltfOptions);

// Report any errors if there are any
Expand Down
5 changes: 3 additions & 2 deletions CesiumGltf/include/CesiumGltf/AccessorUtility.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,9 @@ getIndexAccessorView(const Model& model, const MeshPrimitive& primitive);
/**
* Visitor that retrieves the vertex indices from the given accessor type
* corresponding to a given face index. These indices are returned as an array
* of int64_ts. This should be initialized with the index of the face and the
* total number of vertices in the primitive.
* of int64_ts. This should be initialized with the index of the face, the
* total number of vertices in the primitive, and the
* `CesiumGltf::MeshPrimitive::Mode` of the primitive.
*
* -1 is used to indicate errors retrieving the index, e.g., if the given
* index was out-of-bounds.
Expand Down
16 changes: 16 additions & 0 deletions CesiumGltf/include/CesiumGltf/FeatureIdTextureView.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ enum class FeatureIdTextureViewStatus {
*/
ErrorInvalidImage,

/**
* @brief This feature ID texture has a sampler index that doesn't exist in
* the glTF.
*/
ErrorInvalidSampler,

/**
* @brief This feature ID texture has an empty image.
*/
Expand Down Expand Up @@ -113,6 +119,15 @@ class FeatureIdTextureView {
*/
const ImageCesium* getImage() const { return _pImage; }

/**
* @brief Get the sampler describing how to sample the data from the
* feature ID texture.
*
* This will be nullptr if the feature ID texture view runs into
* problems during construction.
*/
const Sampler* getSampler() const noexcept { return this->_pSampler; }

/**
* @brief Get the channels of this feature ID texture. The channels represent
* the bytes of the actual feature ID, in little-endian order.
Expand All @@ -131,5 +146,6 @@ class FeatureIdTextureView {
std::vector<int64_t> _channels;

const ImageCesium* _pImage;
const Sampler* _pSampler;
};
} // namespace CesiumGltf
4 changes: 1 addition & 3 deletions CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "CesiumGltf/PropertyTypeTraits.h"
#include "CesiumGltf/PropertyView.h"
#include "CesiumGltf/Sampler.h"
#include "CesiumGltf/SamplerUtility.h"

#include <array>
#include <cassert>
Expand Down Expand Up @@ -190,9 +191,6 @@ ElementType assembleValueFromChannels(const gsl::span<uint8_t> bytes) noexcept {
}
}

double applySamplerWrapS(const double u, const int32_t wrapS);
double applySamplerWrapT(const double v, const int32_t wrapT);

std::array<uint8_t, 4> sampleNearestPixel(
const ImageCesium& image,
const std::vector<int64_t>& channels,
Expand Down
8 changes: 8 additions & 0 deletions CesiumGltf/include/CesiumGltf/SamplerUtility.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#pragma once

#include <cstdint>

namespace CesiumGltf {
double applySamplerWrapS(const double u, const int32_t wrapS);
double applySamplerWrapT(const double v, const int32_t wrapT);
} // namespace CesiumGltf
20 changes: 17 additions & 3 deletions CesiumGltf/src/FeatureIdTextureView.cpp
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
#include "CesiumGltf/FeatureIdTextureView.h"

#include "CesiumGltf/SamplerUtility.h"

namespace CesiumGltf {
FeatureIdTextureView::FeatureIdTextureView() noexcept
: _status(FeatureIdTextureViewStatus::ErrorUninitialized),
_texCoordSetIndex(0),
_channels(),
_pImage(nullptr) {}
_pImage(nullptr),
_pSampler(nullptr) {}

FeatureIdTextureView::FeatureIdTextureView(
const Model& model,
const FeatureIdTexture& featureIdTexture) noexcept
: _status(FeatureIdTextureViewStatus::ErrorUninitialized),
_texCoordSetIndex(featureIdTexture.texCoord),
_channels(),
_pImage(nullptr) {
_pImage(nullptr),
_pSampler(nullptr) {
int32_t textureIndex = featureIdTexture.index;
if (textureIndex < 0 ||
static_cast<size_t>(textureIndex) >= model.textures.size()) {
Expand All @@ -28,13 +32,20 @@ FeatureIdTextureView::FeatureIdTextureView(
return;
}

// Ignore the texture's sampler, we will always use nearest pixel sampling.
this->_pImage = &model.images[static_cast<size_t>(texture.source)].cesium;
if (this->_pImage->width < 1 || this->_pImage->height < 1) {
this->_status = FeatureIdTextureViewStatus::ErrorEmptyImage;
return;
}

if (texture.sampler < 0 ||
static_cast<size_t>(texture.sampler) >= model.samplers.size()) {
this->_status = FeatureIdTextureViewStatus::ErrorInvalidSampler;
return;
}

this->_pSampler = &model.samplers[static_cast<size_t>(texture.sampler)];

// TODO: once compressed texture support is merged, check that the image is
// decompressed here.

Expand Down Expand Up @@ -68,6 +79,9 @@ int64_t FeatureIdTextureView::getFeatureID(double u, double v) const noexcept {
return -1;
}

u = applySamplerWrapS(u, this->_pSampler->wrapS);
v = applySamplerWrapT(v, this->_pSampler->wrapT);

// Always use nearest filtering, and use std::floor instead of std::round.
// This is because filtering is supposed to consider the pixel centers. But
// memory access here acts as sampling the beginning of the pixel. Example:
Expand Down
36 changes: 0 additions & 36 deletions CesiumGltf/src/PropertyTexturePropertyView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,42 +22,6 @@ const PropertyViewStatusType
const PropertyViewStatusType
PropertyTexturePropertyViewStatus::ErrorChannelsAndTypeMismatch;

double applySamplerWrapS(const double u, const int32_t wrapS) {
if (wrapS == Sampler::WrapS::REPEAT) {
double integral = 0;
double fraction = std::modf(u, &integral);
return fraction < 0 ? 1.0 - fraction : fraction;
}

if (wrapS == Sampler::WrapS::MIRRORED_REPEAT) {
double integral = 0;
double fraction = std::abs(std::modf(u, &integral));
int64_t integer = static_cast<int64_t>(std::abs(integral));
// If the integer part is odd, the direction is reversed.
return integer % 2 == 1 ? 1.0 - fraction : fraction;
}

return glm::clamp(u, 0.0, 1.0);
}

double applySamplerWrapT(const double v, const int32_t wrapT) {
if (wrapT == Sampler::WrapT::REPEAT) {
double integral = 0;
double fraction = std::modf(v, &integral);
return fraction < 0 ? 1.0 - fraction : fraction;
}

if (wrapT == Sampler::WrapT::MIRRORED_REPEAT) {
double integral = 0;
double fraction = std::abs(std::modf(v, &integral));
int64_t integer = static_cast<int64_t>(std::abs(integral));
// If the integer part is odd, the direction is reversed.
return integer % 2 == 1 ? 1.0 - fraction : fraction;
}

return glm::clamp(v, 0.0, 1.0);
}

std::array<uint8_t, 4> sampleNearestPixel(
const ImageCesium& image,
const std::vector<int64_t>& channels,
Expand Down
Loading

0 comments on commit a56def2

Please sign in to comment.