Merge remote-tracking branch 'origin/i3dm-2024' into i3dm-2024
This commit is contained in:
commit
c6b5321d0a
|
|
@ -10,6 +10,8 @@
|
|||
- `EXT_mesh_gpu_instancing`
|
||||
- `CESIUM_primitive_outline`
|
||||
- `CESIUM_tile_edges`
|
||||
- Fixed a bug in `GltfUtilities::compactBuffer` where it would not preserve the alignment of the bufferViews.
|
||||
- The `collapseToSingleBuffer` and `moveBufferContent` functions in `GltfUtilities` now align to an 8-byte boundary rather than a 4-byte boundary, because bufferViews associated with some glTF extensions require this larger alignment.
|
||||
|
||||
### v0.35.0 - 2024-05-01
|
||||
|
||||
|
|
|
|||
|
|
@ -11,12 +11,12 @@
|
|||
#include <optional>
|
||||
|
||||
namespace Cesium3DTilesContent {
|
||||
struct ConverterSubprocessor;
|
||||
struct AssetFetcher;
|
||||
|
||||
struct B3dmToGltfConverter {
|
||||
static CesiumAsync::Future<GltfConverterResult> convert(
|
||||
const gsl::span<const std::byte>& b3dmBinary,
|
||||
const CesiumGltfReader::GltfReaderOptions& options,
|
||||
const ConverterSubprocessor& subprocessor);
|
||||
const AssetFetcher& assetFetcher);
|
||||
};
|
||||
} // namespace Cesium3DTilesContent
|
||||
|
|
|
|||
|
|
@ -9,20 +9,19 @@
|
|||
#include <cstddef>
|
||||
|
||||
namespace Cesium3DTilesContent {
|
||||
struct ConverterSubprocessor;
|
||||
struct AssetFetcher;
|
||||
|
||||
struct BinaryToGltfConverter {
|
||||
public:
|
||||
static CesiumAsync::Future<GltfConverterResult> convert(
|
||||
const gsl::span<const std::byte>& gltfBinary,
|
||||
const CesiumGltfReader::GltfReaderOptions& options,
|
||||
const ConverterSubprocessor& subProcessor);
|
||||
const AssetFetcher& assetFetcher);
|
||||
|
||||
private:
|
||||
static GltfConverterResult convertImmediate(
|
||||
const gsl::span<const std::byte>& gltfBinary,
|
||||
const CesiumGltfReader::GltfReaderOptions& options,
|
||||
const ConverterSubprocessor& subProcessor);
|
||||
const CesiumGltfReader::GltfReaderOptions& options);
|
||||
static CesiumGltfReader::GltfReader _gltfReader;
|
||||
};
|
||||
} // namespace Cesium3DTilesContent
|
||||
|
|
|
|||
|
|
@ -9,12 +9,12 @@
|
|||
#include <cstddef>
|
||||
|
||||
namespace Cesium3DTilesContent {
|
||||
struct ConverterSubprocessor;
|
||||
struct AssetFetcher;
|
||||
|
||||
struct CmptToGltfConverter {
|
||||
static CesiumAsync::Future<GltfConverterResult> convert(
|
||||
const gsl::span<const std::byte>& cmptBinary,
|
||||
const CesiumGltfReader::GltfReaderOptions& options,
|
||||
const ConverterSubprocessor&);
|
||||
const AssetFetcher& assetFetcher);
|
||||
};
|
||||
} // namespace Cesium3DTilesContent
|
||||
|
|
|
|||
|
|
@ -15,12 +15,17 @@
|
|||
|
||||
namespace Cesium3DTilesContent {
|
||||
|
||||
struct ByteResult {
|
||||
std::vector<std::byte> bytes;
|
||||
CesiumUtility::ErrorList errorList;
|
||||
};
|
||||
|
||||
/**
|
||||
* Data required to make a recursive request to fetch an asset, mostly for the
|
||||
* benefit of I3dm files.
|
||||
* Object that makes a recursive request to fetch an asset, mostly for the
|
||||
* benefit of i3dm files.
|
||||
*/
|
||||
struct CESIUM3DTILESCONTENT_API ConverterSubprocessor {
|
||||
ConverterSubprocessor(
|
||||
struct CESIUM3DTILESCONTENT_API AssetFetcher {
|
||||
AssetFetcher(
|
||||
const CesiumAsync::AsyncSystem& asyncSystem_,
|
||||
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor_,
|
||||
const std::string& baseUrl_,
|
||||
|
|
@ -31,10 +36,13 @@ struct CESIUM3DTILESCONTENT_API ConverterSubprocessor {
|
|||
baseUrl(baseUrl_),
|
||||
tileTransform(tileTransform_),
|
||||
requestHeaders(requestHeaders_) {}
|
||||
|
||||
CesiumAsync::Future<ByteResult> get(const std::string& relativeUrl) const;
|
||||
|
||||
const CesiumAsync::AsyncSystem& asyncSystem;
|
||||
const std::shared_ptr<CesiumAsync::IAssetAccessor> pAssetAccessor;
|
||||
const std::string baseUrl;
|
||||
glm::dmat4 tileTransform;
|
||||
glm::dmat4 tileTransform; // For ENU transforms in i3dm
|
||||
const std::vector<CesiumAsync::IAssetAccessor::THeader>& requestHeaders;
|
||||
};
|
||||
|
||||
|
|
@ -61,7 +69,7 @@ public:
|
|||
using ConverterFunction = CesiumAsync::Future<GltfConverterResult> (*)(
|
||||
const gsl::span<const std::byte>& content,
|
||||
const CesiumGltfReader::GltfReaderOptions& options,
|
||||
const ConverterSubprocessor& subprocessor);
|
||||
const AssetFetcher& subprocessor);
|
||||
|
||||
/**
|
||||
* @brief Register the given function for the given magic header.
|
||||
|
|
@ -144,14 +152,16 @@ public:
|
|||
* the converter.
|
||||
* @param content The tile binary content that may contains the magic header
|
||||
* to look up the converter and is used to convert to gltf model.
|
||||
* @param options The {@link CesiumGltfReader::GltfReaderOptions} for how to read a glTF.
|
||||
* @param options The {@link CesiumGltfReader::GltfReaderOptions} for how to
|
||||
* read a glTF.
|
||||
* @param assetFetcher An object that can perform recursive asset requests.
|
||||
* @return The {@link GltfConverterResult} that stores the gltf model converted from the binary data.
|
||||
*/
|
||||
static CesiumAsync::Future<GltfConverterResult> convert(
|
||||
const std::string& filePath,
|
||||
const gsl::span<const std::byte>& content,
|
||||
const CesiumGltfReader::GltfReaderOptions& options,
|
||||
const ConverterSubprocessor& subprocessor);
|
||||
const AssetFetcher& assetFetcher);
|
||||
|
||||
/**
|
||||
* @brief Creates the {@link GltfConverterResult} from the given
|
||||
|
|
@ -170,13 +180,15 @@ public:
|
|||
*
|
||||
* @param content The tile binary content that may contains the magic header
|
||||
* to look up the converter and is used to convert to gltf model.
|
||||
* @param options The {@link CesiumGltfReader::GltfReaderOptions} for how to read a glTF.
|
||||
* @param options The {@link CesiumGltfReader::GltfReaderOptions} for how to
|
||||
* read a glTF.
|
||||
* @param assetFetcher An object that can perform recursive asset requests.
|
||||
* @return The {@link GltfConverterResult} that stores the gltf model converted from the binary data.
|
||||
*/
|
||||
static CesiumAsync::Future<GltfConverterResult> convert(
|
||||
const gsl::span<const std::byte>& content,
|
||||
const CesiumGltfReader::GltfReaderOptions& options,
|
||||
const ConverterSubprocessor& subprocessor);
|
||||
const AssetFetcher& assetFetcher);
|
||||
|
||||
private:
|
||||
static std::string toLowerCase(const std::string_view& str);
|
||||
|
|
|
|||
|
|
@ -11,12 +11,12 @@
|
|||
#include <optional>
|
||||
|
||||
namespace Cesium3DTilesContent {
|
||||
struct ConverterSubprocessor;
|
||||
struct AssetFetcher;
|
||||
|
||||
struct I3dmToGltfConverter {
|
||||
static CesiumAsync::Future<GltfConverterResult> convert(
|
||||
const gsl::span<const std::byte>& instancesBinary,
|
||||
const CesiumGltfReader::GltfReaderOptions& options,
|
||||
const ConverterSubprocessor& subprocessor);
|
||||
const AssetFetcher& assetFetcher);
|
||||
};
|
||||
} // namespace Cesium3DTilesContent
|
||||
|
|
|
|||
|
|
@ -21,16 +21,8 @@ class Buffer;
|
|||
|
||||
namespace Cesium3DTilesContent {
|
||||
|
||||
struct ByteResult {
|
||||
std::vector<std::byte> bytes;
|
||||
CesiumUtility::ErrorList errorList;
|
||||
};
|
||||
|
||||
CesiumAsync::Future<ByteResult>
|
||||
get(const ConverterSubprocessor& subprocessor, const std::string& relativeUrl);
|
||||
|
||||
namespace LegacyUtilities {
|
||||
std::optional<uint32_t> parseOffset(
|
||||
std::optional<uint32_t> parseOffsetForSemantic(
|
||||
const rapidjson::Document& document,
|
||||
const char* semantic,
|
||||
CesiumUtility::ErrorList& errorList);
|
||||
|
|
@ -84,9 +76,7 @@ parseArrayValueDVec3(const rapidjson::Value& arrayValue);
|
|||
std::optional<glm::dvec3>
|
||||
parseArrayValueDVec3(const rapidjson::Document& document, const char* name);
|
||||
|
||||
int32_t createBufferInGltf(CesiumGltf::Model& gltf);
|
||||
int32_t
|
||||
createBufferInGltf(CesiumGltf::Model& gltf, std::vector<std::byte>&& buffer);
|
||||
int32_t createBufferInGltf(CesiumGltf::Model& gltf, std::vector<std::byte> buffer = {});
|
||||
|
||||
int32_t createBufferViewInGltf(
|
||||
CesiumGltf::Model& gltf,
|
||||
|
|
@ -101,27 +91,27 @@ int32_t createAccessorInGltf(
|
|||
const int64_t count,
|
||||
const std::string type);
|
||||
|
||||
void applyRTC(CesiumGltf::Model& gltf, const glm::dvec3& rtc);
|
||||
void applyRtcToNodes(CesiumGltf::Model& gltf, const glm::dvec3& rtc);
|
||||
|
||||
template <typename GLMType, typename GLTFType>
|
||||
GLMType toGlm(const GLTFType& gltfVal);
|
||||
template <typename GlmType, typename GLTFType>
|
||||
GlmType toGlm(const GLTFType& gltfVal);
|
||||
|
||||
template <typename GLMType, typename COMPONENTType>
|
||||
GLMType toGlm(const CesiumGltf::AccessorTypes::VEC3<COMPONENTType>& gltfVal) {
|
||||
return GLMType(gltfVal.value[0], gltfVal.value[1], gltfVal.value[2]);
|
||||
template <typename GlmType, typename ComponentType>
|
||||
GlmType toGlm(const CesiumGltf::AccessorTypes::VEC3<ComponentType>& gltfVal) {
|
||||
return GlmType(gltfVal.value[0], gltfVal.value[1], gltfVal.value[2]);
|
||||
}
|
||||
|
||||
template <typename GLMType, typename COMPONENTType>
|
||||
GLMType
|
||||
toGlmQuat(const CesiumGltf::AccessorTypes::VEC4<COMPONENTType>& gltfVal) {
|
||||
if constexpr (std::is_same<COMPONENTType, float>()) {
|
||||
return GLMType(
|
||||
template <typename GlmType, typename ComponentType>
|
||||
GlmType
|
||||
toGlmQuat(const CesiumGltf::AccessorTypes::VEC4<ComponentType>& gltfVal) {
|
||||
if constexpr (std::is_same<ComponentType, float>()) {
|
||||
return GlmType(
|
||||
gltfVal.value[3],
|
||||
gltfVal.value[0],
|
||||
gltfVal.value[1],
|
||||
gltfVal.value[2]);
|
||||
} else {
|
||||
return GLMType(
|
||||
return GlmType(
|
||||
CesiumGltf::normalize(gltfVal.value[3]),
|
||||
CesiumGltf::normalize(gltfVal.value[0]),
|
||||
CesiumGltf::normalize(gltfVal.value[1]),
|
||||
|
|
|
|||
|
|
@ -11,12 +11,12 @@
|
|||
#include <optional>
|
||||
|
||||
namespace Cesium3DTilesContent {
|
||||
struct ConverterSubprocessor;
|
||||
struct AssetFetcher;
|
||||
|
||||
struct PntsToGltfConverter {
|
||||
static CesiumAsync::Future<GltfConverterResult> convert(
|
||||
const gsl::span<const std::byte>& pntsBinary,
|
||||
const CesiumGltfReader::GltfReaderOptions& options,
|
||||
const ConverterSubprocessor& subprocessor);
|
||||
const AssetFetcher& assetFetcher);
|
||||
};
|
||||
} // namespace Cesium3DTilesContent
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ CesiumAsync::Future<GltfConverterResult> convertB3dmContentToGltf(
|
|||
const B3dmHeader& header,
|
||||
uint32_t headerLength,
|
||||
const CesiumGltfReader::GltfReaderOptions& options,
|
||||
const ConverterSubprocessor& subprocessor) {
|
||||
const AssetFetcher& assetFetcher) {
|
||||
const uint32_t glbStart = headerLength + header.featureTableJsonByteLength +
|
||||
header.featureTableBinaryByteLength +
|
||||
header.batchTableJsonByteLength +
|
||||
|
|
@ -131,13 +131,13 @@ CesiumAsync::Future<GltfConverterResult> convertB3dmContentToGltf(
|
|||
result.errors.emplaceError(
|
||||
"The B3DM is invalid because the start of the "
|
||||
"glTF model is after the end of the entire B3DM.");
|
||||
return subprocessor.asyncSystem.createResolvedFuture(std::move(result));
|
||||
return assetFetcher.asyncSystem.createResolvedFuture(std::move(result));
|
||||
}
|
||||
|
||||
const gsl::span<const std::byte> glbData =
|
||||
b3dmBinary.subspan(glbStart, glbEnd - glbStart);
|
||||
|
||||
return BinaryToGltfConverter::convert(glbData, options, subprocessor);
|
||||
return BinaryToGltfConverter::convert(glbData, options, assetFetcher);
|
||||
}
|
||||
|
||||
rapidjson::Document parseFeatureTableJsonData(
|
||||
|
|
@ -232,13 +232,13 @@ void convertB3dmMetadataToGltfStructuralMetadata(
|
|||
CesiumAsync::Future<GltfConverterResult> B3dmToGltfConverter::convert(
|
||||
const gsl::span<const std::byte>& b3dmBinary,
|
||||
const CesiumGltfReader::GltfReaderOptions& options,
|
||||
const ConverterSubprocessor& subprocessor) {
|
||||
const AssetFetcher& assetFetcher) {
|
||||
GltfConverterResult result;
|
||||
B3dmHeader header;
|
||||
uint32_t headerLength = 0;
|
||||
parseB3dmHeader(b3dmBinary, header, headerLength, result);
|
||||
if (result.errors) {
|
||||
return subprocessor.asyncSystem.createResolvedFuture(std::move(result));
|
||||
return assetFetcher.asyncSystem.createResolvedFuture(std::move(result));
|
||||
}
|
||||
|
||||
return convertB3dmContentToGltf(
|
||||
|
|
@ -246,7 +246,7 @@ CesiumAsync::Future<GltfConverterResult> B3dmToGltfConverter::convert(
|
|||
header,
|
||||
headerLength,
|
||||
options,
|
||||
subprocessor)
|
||||
assetFetcher)
|
||||
.thenImmediately(
|
||||
[b3dmBinary, header, headerLength](GltfConverterResult&& glbResult) {
|
||||
if (!glbResult.errors) {
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@ CesiumGltfReader::GltfReader BinaryToGltfConverter::_gltfReader;
|
|||
|
||||
GltfConverterResult BinaryToGltfConverter::convertImmediate(
|
||||
const gsl::span<const std::byte>& gltfBinary,
|
||||
const CesiumGltfReader::GltfReaderOptions& options,
|
||||
const ConverterSubprocessor&) {
|
||||
const CesiumGltfReader::GltfReaderOptions& options) {
|
||||
CesiumGltfReader::GltfReaderResult loadedGltf =
|
||||
_gltfReader.readGltf(gltfBinary, options);
|
||||
|
||||
|
|
@ -21,8 +20,8 @@ GltfConverterResult BinaryToGltfConverter::convertImmediate(
|
|||
CesiumAsync::Future<GltfConverterResult> BinaryToGltfConverter::convert(
|
||||
const gsl::span<const std::byte>& gltfBinary,
|
||||
const CesiumGltfReader::GltfReaderOptions& options,
|
||||
const ConverterSubprocessor& subprocessor) {
|
||||
return subprocessor.asyncSystem.createResolvedFuture(
|
||||
convertImmediate(gltfBinary, options, subprocessor));
|
||||
const AssetFetcher& assetFetcher) {
|
||||
return assetFetcher.asyncSystem.createResolvedFuture(
|
||||
convertImmediate(gltfBinary, options));
|
||||
}
|
||||
} // namespace Cesium3DTilesContent
|
||||
|
|
|
|||
|
|
@ -25,11 +25,11 @@ static_assert(sizeof(InnerHeader) == 12);
|
|||
CesiumAsync::Future<GltfConverterResult> CmptToGltfConverter::convert(
|
||||
const gsl::span<const std::byte>& cmptBinary,
|
||||
const CesiumGltfReader::GltfReaderOptions& options,
|
||||
const ConverterSubprocessor& subProcessor) {
|
||||
const AssetFetcher& assetFetcher) {
|
||||
GltfConverterResult result;
|
||||
if (cmptBinary.size() < sizeof(CmptHeader)) {
|
||||
result.errors.emplaceWarning("Composite tile must be at least 16 bytes.");
|
||||
return subProcessor.asyncSystem.createResolvedFuture(std::move(result));
|
||||
return assetFetcher.asyncSystem.createResolvedFuture(std::move(result));
|
||||
}
|
||||
|
||||
const CmptHeader* pHeader =
|
||||
|
|
@ -37,14 +37,14 @@ CesiumAsync::Future<GltfConverterResult> CmptToGltfConverter::convert(
|
|||
if (std::string(pHeader->magic, 4) != "cmpt") {
|
||||
result.errors.emplaceWarning(
|
||||
"Composite tile does not have the expected magic vaue 'cmpt'.");
|
||||
return subProcessor.asyncSystem.createResolvedFuture(std::move(result));
|
||||
return assetFetcher.asyncSystem.createResolvedFuture(std::move(result));
|
||||
}
|
||||
|
||||
if (pHeader->version != 1) {
|
||||
result.errors.emplaceWarning(fmt::format(
|
||||
"Unsupported composite tile version {}.",
|
||||
pHeader->version));
|
||||
return subProcessor.asyncSystem.createResolvedFuture(std::move(result));
|
||||
return assetFetcher.asyncSystem.createResolvedFuture(std::move(result));
|
||||
}
|
||||
|
||||
if (pHeader->byteLength > cmptBinary.size()) {
|
||||
|
|
@ -52,7 +52,7 @@ CesiumAsync::Future<GltfConverterResult> CmptToGltfConverter::convert(
|
|||
"Composite tile byteLength is {} but only {} bytes are available.",
|
||||
pHeader->byteLength,
|
||||
cmptBinary.size()));
|
||||
return subProcessor.asyncSystem.createResolvedFuture(std::move(result));
|
||||
return assetFetcher.asyncSystem.createResolvedFuture(std::move(result));
|
||||
}
|
||||
|
||||
std::vector<CesiumAsync::Future<GltfConverterResult>> innerTiles;
|
||||
|
|
@ -80,7 +80,7 @@ CesiumAsync::Future<GltfConverterResult> CmptToGltfConverter::convert(
|
|||
pos += pInner->byteLength;
|
||||
|
||||
innerTiles.emplace_back(
|
||||
GltfConverters::convert(innerData, options, subProcessor));
|
||||
GltfConverters::convert(innerData, options, assetFetcher));
|
||||
}
|
||||
|
||||
uint32_t tilesLength = pHeader->tilesLength;
|
||||
|
|
@ -90,10 +90,10 @@ CesiumAsync::Future<GltfConverterResult> CmptToGltfConverter::convert(
|
|||
"Composite tile does not contain any loadable inner "
|
||||
"tiles.");
|
||||
}
|
||||
return subProcessor.asyncSystem.createResolvedFuture(std::move(result));
|
||||
return assetFetcher.asyncSystem.createResolvedFuture(std::move(result));
|
||||
}
|
||||
|
||||
return subProcessor.asyncSystem.all(std::move(innerTiles))
|
||||
return assetFetcher.asyncSystem.all(std::move(innerTiles))
|
||||
.thenImmediately([](std::vector<GltfConverterResult>&& innerResults) {
|
||||
if (innerResults.size() == 1) {
|
||||
return innerResults[0];
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
#include <CesiumAsync/IAssetResponse.h>
|
||||
#include <Cesium3DTilesContent/GltfConverters.h>
|
||||
#include <CesiumUtility/Uri.h>
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
|
|
@ -41,17 +43,17 @@ CesiumAsync::Future<GltfConverterResult> GltfConverters::convert(
|
|||
const std::string& filePath,
|
||||
const gsl::span<const std::byte>& content,
|
||||
const CesiumGltfReader::GltfReaderOptions& options,
|
||||
const ConverterSubprocessor& subprocessor) {
|
||||
const AssetFetcher& assetFetcher) {
|
||||
std::string magic;
|
||||
auto converterFun = getConverterByMagic(content, magic);
|
||||
if (converterFun) {
|
||||
return converterFun(content, options, subprocessor);
|
||||
return converterFun(content, options, assetFetcher);
|
||||
}
|
||||
|
||||
std::string fileExtension;
|
||||
converterFun = getConverterByFileExtension(filePath, fileExtension);
|
||||
if (converterFun) {
|
||||
return converterFun(content, options, subprocessor);
|
||||
return converterFun(content, options, assetFetcher);
|
||||
}
|
||||
|
||||
ErrorList errors;
|
||||
|
|
@ -61,18 +63,18 @@ CesiumAsync::Future<GltfConverterResult> GltfConverters::convert(
|
|||
fileExtension,
|
||||
magic));
|
||||
|
||||
return subprocessor.asyncSystem.createResolvedFuture(
|
||||
return assetFetcher.asyncSystem.createResolvedFuture(
|
||||
GltfConverterResult{std::nullopt, std::move(errors)});
|
||||
}
|
||||
|
||||
CesiumAsync::Future<GltfConverterResult> GltfConverters::convert(
|
||||
const gsl::span<const std::byte>& content,
|
||||
const CesiumGltfReader::GltfReaderOptions& options,
|
||||
const ConverterSubprocessor& subprocessor) {
|
||||
const AssetFetcher& assetFetcher) {
|
||||
std::string magic;
|
||||
auto converter = getConverterByMagic(content, magic);
|
||||
if (converter) {
|
||||
return converter(content, options, subprocessor);
|
||||
return converter(content, options, assetFetcher);
|
||||
}
|
||||
|
||||
ErrorList errors;
|
||||
|
|
@ -80,7 +82,7 @@ CesiumAsync::Future<GltfConverterResult> GltfConverters::convert(
|
|||
"No loader registered for tile with magic value '{}'",
|
||||
magic));
|
||||
|
||||
return subprocessor.asyncSystem.createResolvedFuture(
|
||||
return assetFetcher.asyncSystem.createResolvedFuture(
|
||||
GltfConverterResult{std::nullopt, std::move(errors)});
|
||||
}
|
||||
|
||||
|
|
@ -132,4 +134,38 @@ GltfConverters::ConverterFunction GltfConverters::getConverterByMagic(
|
|||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CesiumAsync::Future<ByteResult>
|
||||
AssetFetcher::get(const std::string& relativeUrl) const {
|
||||
auto resolvedUrl = Uri::resolve(baseUrl, relativeUrl);
|
||||
return pAssetAccessor->get(asyncSystem, resolvedUrl, requestHeaders)
|
||||
.thenImmediately(
|
||||
[asyncSystem = asyncSystem](
|
||||
std::shared_ptr<CesiumAsync::IAssetRequest>&& pCompletedRequest) {
|
||||
const CesiumAsync::IAssetResponse* pResponse =
|
||||
pCompletedRequest->response();
|
||||
ByteResult byteResult;
|
||||
const auto& url = pCompletedRequest->url();
|
||||
if (!pResponse) {
|
||||
byteResult.errorList.emplaceError(fmt::format(
|
||||
"Did not receive a valid response for asset {}",
|
||||
url));
|
||||
return asyncSystem.createResolvedFuture(std::move(byteResult));
|
||||
}
|
||||
uint16_t statusCode = pResponse->statusCode();
|
||||
if (statusCode != 0 && (statusCode < 200 || statusCode >= 300)) {
|
||||
byteResult.errorList.emplaceError(fmt::format(
|
||||
"Received status code {} for asset {}",
|
||||
statusCode,
|
||||
url));
|
||||
return asyncSystem.createResolvedFuture(std::move(byteResult));
|
||||
}
|
||||
gsl::span<const std::byte> asset = pResponse->data();
|
||||
std::copy(
|
||||
asset.begin(),
|
||||
asset.end(),
|
||||
std::back_inserter(byteResult.bytes));
|
||||
return asyncSystem.createResolvedFuture(std::move(byteResult));
|
||||
});
|
||||
}
|
||||
} // namespace Cesium3DTilesContent
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ namespace Cesium3DTilesContent {
|
|||
using namespace LegacyUtilities;
|
||||
|
||||
namespace {
|
||||
struct InstancesHeader {
|
||||
struct I3dmHeader {
|
||||
unsigned char magic[4] = {0, 0, 0, 0};
|
||||
uint32_t version = 0;
|
||||
uint32_t byteLength = 0;
|
||||
|
|
@ -50,10 +50,13 @@ struct DecodedInstances {
|
|||
|
||||
// Instance positions may arrive in ECEF coordinates or with other large
|
||||
// displacements that will cause problems during rendering. Determine the mean
|
||||
// position of the instances and render them relative to that, creating a new
|
||||
// RTC center.
|
||||
// position of the instances and reposition them relative to it, thus creating
|
||||
// a new RTC center.
|
||||
//
|
||||
// If an RTC center value is already present, then the newly-computed center is
|
||||
// added to it.
|
||||
|
||||
void rebaseInstances(DecodedInstances& decodedInstances) {
|
||||
void repositionInstances(DecodedInstances& decodedInstances) {
|
||||
if (decodedInstances.positions.empty()) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -75,22 +78,22 @@ void rebaseInstances(DecodedInstances& decodedInstances) {
|
|||
decodedInstances.rtcCenter = newCenter;
|
||||
}
|
||||
|
||||
void parseInstancesHeader(
|
||||
void parseI3dmHeader(
|
||||
const gsl::span<const std::byte>& instancesBinary,
|
||||
InstancesHeader& header,
|
||||
I3dmHeader& header,
|
||||
uint32_t& headerLength,
|
||||
GltfConverterResult& result) {
|
||||
if (instancesBinary.size() < sizeof(InstancesHeader)) {
|
||||
if (instancesBinary.size() < sizeof(I3dmHeader)) {
|
||||
result.errors.emplaceError("The I3DM is invalid because it is too small to "
|
||||
"include a I3DM header.");
|
||||
return;
|
||||
}
|
||||
|
||||
const InstancesHeader* pHeader =
|
||||
reinterpret_cast<const InstancesHeader*>(instancesBinary.data());
|
||||
const I3dmHeader* pHeader =
|
||||
reinterpret_cast<const I3dmHeader*>(instancesBinary.data());
|
||||
|
||||
header = *pHeader;
|
||||
headerLength = sizeof(InstancesHeader);
|
||||
headerLength = sizeof(I3dmHeader);
|
||||
|
||||
if (pHeader->version != 1) {
|
||||
result.errors.emplaceError(fmt::format(
|
||||
|
|
@ -107,7 +110,7 @@ void parseInstancesHeader(
|
|||
}
|
||||
}
|
||||
|
||||
struct InstanceContent {
|
||||
struct I3dmContent {
|
||||
uint32_t instancesLength = 0;
|
||||
std::optional<glm::dvec3> rtcCenter;
|
||||
std::optional<glm::dvec3> quantizedVolumeOffset;
|
||||
|
|
@ -124,7 +127,6 @@ struct InstanceContent {
|
|||
std::optional<uint32_t> scale;
|
||||
std::optional<uint32_t> scaleNonUniform;
|
||||
std::optional<uint32_t> batchId;
|
||||
// batch table format?
|
||||
CesiumUtility::ErrorList errors;
|
||||
};
|
||||
|
||||
|
|
@ -184,7 +186,7 @@ glm::quat rotationFromUpRight(const glm::vec3& up, const glm::vec3& right) {
|
|||
return upRot * rightRot;
|
||||
}
|
||||
|
||||
struct ConvertResult {
|
||||
struct ConvertedI3dm {
|
||||
GltfConverterResult gltfResult;
|
||||
DecodedInstances decodedInstances;
|
||||
};
|
||||
|
|
@ -201,176 +203,230 @@ struct ConvertResult {
|
|||
table, hashed by mesh transform.
|
||||
+ Add the instance transforms to the glTF buffers, buffer views, and
|
||||
accessors.
|
||||
+ Metadata / feature id?
|
||||
+ Future work: Metadata / feature id?
|
||||
*/
|
||||
|
||||
CesiumAsync::Future<ConvertResult> convertInstancesContent(
|
||||
const gsl::span<const std::byte>& instancesBinary,
|
||||
const InstancesHeader& header,
|
||||
uint32_t headerLength,
|
||||
const CesiumGltfReader::GltfReaderOptions& options,
|
||||
const ConverterSubprocessor& subprocessor,
|
||||
GltfConverterResult& result) {
|
||||
ConvertResult subResult;
|
||||
DecodedInstances& decodedInstances = subResult.decodedInstances;
|
||||
subResult.gltfResult = result;
|
||||
auto finishEarly = [&]() {
|
||||
return subprocessor.asyncSystem.createResolvedFuture(std::move(subResult));
|
||||
};
|
||||
if (header.featureTableJsonByteLength == 0 ||
|
||||
header.featureTableBinaryByteLength == 0) {
|
||||
return finishEarly();
|
||||
}
|
||||
const uint32_t glTFStart = headerLength + header.featureTableJsonByteLength +
|
||||
header.featureTableBinaryByteLength +
|
||||
header.batchTableJsonByteLength +
|
||||
header.batchTableBinaryByteLength;
|
||||
const uint32_t glTFEnd = header.byteLength;
|
||||
auto gltfData = instancesBinary.subspan(glTFStart, glTFEnd - glTFStart);
|
||||
std::optional<CesiumAsync::Future<ByteResult>> assetFuture;
|
||||
auto featureTableJsonData =
|
||||
instancesBinary.subspan(headerLength, header.featureTableJsonByteLength);
|
||||
std::optional<I3dmContent> parseI3dmJson(
|
||||
const gsl::span<const std::byte> featureTableJsonData,
|
||||
CesiumUtility::ErrorList& errors) {
|
||||
rapidjson::Document featureTableJson;
|
||||
featureTableJson.Parse(
|
||||
reinterpret_cast<const char*>(featureTableJsonData.data()),
|
||||
featureTableJsonData.size());
|
||||
if (featureTableJson.HasParseError()) {
|
||||
subResult.gltfResult.errors.emplaceError(fmt::format(
|
||||
"Error when parsing feature table JSON, error code {} at byte offset "
|
||||
"{}",
|
||||
featureTableJson.GetParseError(),
|
||||
featureTableJson.GetErrorOffset()));
|
||||
return finishEarly();
|
||||
errors.emplaceError(fmt::format(
|
||||
"Error when parsing feature table JSON, error code {} at byte offset "
|
||||
"{}",
|
||||
featureTableJson.GetParseError(),
|
||||
featureTableJson.GetErrorOffset()));
|
||||
return {};
|
||||
}
|
||||
InstanceContent parsedContent;
|
||||
I3dmContent parsedContent;
|
||||
// Global semantics
|
||||
if (auto optinstancesLength =
|
||||
if (auto optInstancesLength =
|
||||
getValue<uint32_t>(featureTableJson, "INSTANCES_LENGTH")) {
|
||||
parsedContent.instancesLength = *optinstancesLength;
|
||||
parsedContent.instancesLength = *optInstancesLength;
|
||||
} else {
|
||||
subResult.gltfResult.errors.emplaceError(
|
||||
"Error parsing I3DM feature table, no valid "
|
||||
"INSTANCES_LENGTH was found.");
|
||||
return finishEarly();
|
||||
errors.emplaceError(
|
||||
"Error parsing I3DM feature table, no valid INSTANCES_LENGTH was found.");
|
||||
return {};
|
||||
}
|
||||
parsedContent.rtcCenter =
|
||||
parseArrayValueDVec3(featureTableJson, "RTC_CENTER");
|
||||
decodedInstances.rtcCenter = parsedContent.rtcCenter;
|
||||
|
||||
parsedContent.position =
|
||||
parseOffset(featureTableJson, "POSITION", subResult.gltfResult.errors);
|
||||
if (!parsedContent.position) {
|
||||
if (subResult.gltfResult.errors.hasErrors()) {
|
||||
return finishEarly();
|
||||
}
|
||||
parsedContent.positionQuantized = parseOffset(
|
||||
featureTableJson,
|
||||
"POSITION_QUANTIZED",
|
||||
subResult.gltfResult.errors);
|
||||
if (subResult.gltfResult.errors.hasErrors()) {
|
||||
return finishEarly();
|
||||
}
|
||||
parseOffsetForSemantic(
|
||||
featureTableJson, "POSITION", errors);
|
||||
parsedContent.positionQuantized = parseOffsetForSemantic(
|
||||
featureTableJson,
|
||||
"POSITION_QUANTIZED",
|
||||
errors);
|
||||
if (errors.hasErrors()) {
|
||||
return {};
|
||||
}
|
||||
if (parsedContent.positionQuantized) {
|
||||
// I would have liked to just test !parsedContent.position, but the perfectly
|
||||
// reasonable value of 0 causes the test to be false!
|
||||
if (!(parsedContent.position.has_value() || parsedContent.positionQuantized.has_value())) {
|
||||
errors.emplaceError(
|
||||
"I3dm file contains neither POSITION nor POSITION_QUANTIZED semantics.");
|
||||
return {};
|
||||
}
|
||||
if (parsedContent.positionQuantized.has_value()) {
|
||||
parsedContent.quantizedVolumeOffset =
|
||||
parseArrayValueDVec3(featureTableJson, "QUANTIZED_VOLUME_OFFSET");
|
||||
if (!parsedContent.quantizedVolumeOffset) {
|
||||
subResult.gltfResult.errors.emplaceError(
|
||||
"Error parsing I3DM feature table, No valid "
|
||||
"QUANTIZED_VOLUME_OFFSET property");
|
||||
return finishEarly();
|
||||
if (!parsedContent.quantizedVolumeOffset.has_value()) {
|
||||
errors.emplaceError(
|
||||
"Error parsing I3DM feature table, the I3dm uses quatized positions "
|
||||
"but has no valid QUANTIZED_VOLUME_OFFSET property");
|
||||
return {};
|
||||
}
|
||||
parsedContent.quantizedVolumeScale =
|
||||
parseArrayValueDVec3(featureTableJson, "QUANTIZED_VOLUME_SCALE");
|
||||
if (!parsedContent.quantizedVolumeScale) {
|
||||
subResult.gltfResult.errors.emplaceError(
|
||||
"Error parsing I3DM feature table, No valid "
|
||||
"QUANTIZED_VOLUME_SCALE property");
|
||||
return finishEarly();
|
||||
if (!parsedContent.quantizedVolumeScale.has_value()) {
|
||||
errors.emplaceError(
|
||||
"Error parsing I3DM feature table, the I3dm uses quatized positions "
|
||||
"but has no valid QUANTIZED_VOLUME_SCALE property");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
decodedInstances.rotationENU = false;
|
||||
if (auto optENU = getValue<bool>(featureTableJson, "EAST_NORTH_UP")) {
|
||||
parsedContent.eastNorthUp = *optENU;
|
||||
decodedInstances.rotationENU = *optENU;
|
||||
}
|
||||
parsedContent.normalUp =
|
||||
parseOffset(featureTableJson, "NORMAL_UP", subResult.gltfResult.errors);
|
||||
parsedContent.normalRight = parseOffset(
|
||||
parsedContent.normalUp = parseOffsetForSemantic(
|
||||
featureTableJson,
|
||||
"NORMAL_UP",
|
||||
errors);
|
||||
parsedContent.normalRight = parseOffsetForSemantic(
|
||||
featureTableJson,
|
||||
"NORMAL_RIGHT",
|
||||
subResult.gltfResult.errors);
|
||||
parsedContent.normalUpOct32p = parseOffset(
|
||||
errors);
|
||||
if (errors.hasErrors()) {
|
||||
return {};
|
||||
}
|
||||
if (parsedContent.normalUp.has_value() && !parsedContent.normalRight.has_value()) {
|
||||
errors.emplaceError("I3dm has NORMAL_UP semantic without NORMAL_RIGHT.");
|
||||
return {};
|
||||
}
|
||||
if (!parsedContent.normalUp.has_value() && parsedContent.normalRight.has_value()) {
|
||||
errors.emplaceError("I3dm has NORMAL_RIGHT semantic without NORMAL_UP.");
|
||||
return {};
|
||||
}
|
||||
parsedContent.normalUpOct32p = parseOffsetForSemantic(
|
||||
featureTableJson,
|
||||
"NORMAL_UP_OCT32P",
|
||||
subResult.gltfResult.errors);
|
||||
parsedContent.normalRightOct32p = parseOffset(
|
||||
errors);
|
||||
parsedContent.normalRightOct32p = parseOffsetForSemantic(
|
||||
featureTableJson,
|
||||
"NORMAL_RIGHT_OCT32P",
|
||||
subResult.gltfResult.errors);
|
||||
parsedContent.scale =
|
||||
parseOffset(featureTableJson, "SCALE", subResult.gltfResult.errors);
|
||||
parsedContent.scaleNonUniform = parseOffset(
|
||||
errors);
|
||||
if (errors.hasErrors()) {
|
||||
return {};
|
||||
}
|
||||
if (parsedContent.normalUpOct32p.has_value() && !parsedContent.normalRightOct32p.has_value()) {
|
||||
errors.emplaceError("I3dm has NORMAL_UP_OCT32P semantic without NORMAL_RIGHT_OCT32P.");
|
||||
return {};
|
||||
}
|
||||
if (!parsedContent.normalUpOct32p.has_value() && parsedContent.normalRightOct32p.has_value()) {
|
||||
errors.emplaceError("I3dm has NORMAL_RIGHT_OCT32P semantic without NORMAL_UP_OCT32P.");
|
||||
return {};
|
||||
}
|
||||
parsedContent.scale = parseOffsetForSemantic(
|
||||
featureTableJson,
|
||||
"SCALE",
|
||||
errors);
|
||||
parsedContent.scaleNonUniform = parseOffsetForSemantic(
|
||||
featureTableJson,
|
||||
"SCALE_NON_UNIFORM",
|
||||
subResult.gltfResult.errors);
|
||||
parsedContent.batchId =
|
||||
parseOffset(featureTableJson, "BATCH_ID", subResult.gltfResult.errors);
|
||||
if (subResult.gltfResult.errors.hasErrors()) {
|
||||
errors);
|
||||
parsedContent.batchId = parseOffsetForSemantic(
|
||||
featureTableJson,
|
||||
"BATCH_ID",
|
||||
errors);
|
||||
if (errors.hasErrors()) {
|
||||
return {};
|
||||
}
|
||||
return parsedContent;
|
||||
}
|
||||
|
||||
CesiumAsync::Future<ConvertedI3dm> convertI3dmContent(
|
||||
const gsl::span<const std::byte>& instancesBinary,
|
||||
const I3dmHeader& header,
|
||||
uint32_t headerLength,
|
||||
const CesiumGltfReader::GltfReaderOptions& options,
|
||||
const AssetFetcher& assetFetcher,
|
||||
GltfConverterResult& result) {
|
||||
ConvertedI3dm convertedI3dm;
|
||||
DecodedInstances& decodedInstances = convertedI3dm.decodedInstances;
|
||||
convertedI3dm.gltfResult = result;
|
||||
auto finishEarly = [&]() {
|
||||
return assetFetcher.asyncSystem.createResolvedFuture(std::move(convertedI3dm));
|
||||
};
|
||||
if (header.featureTableJsonByteLength == 0 ||
|
||||
header.featureTableBinaryByteLength == 0) {
|
||||
return finishEarly();
|
||||
}
|
||||
const uint32_t gltfStart = headerLength + header.featureTableJsonByteLength +
|
||||
header.featureTableBinaryByteLength +
|
||||
header.batchTableJsonByteLength +
|
||||
header.batchTableBinaryByteLength;
|
||||
const uint32_t gltfEnd = header.byteLength;
|
||||
auto gltfData = instancesBinary.subspan(gltfStart, gltfEnd - gltfStart);
|
||||
std::optional<CesiumAsync::Future<ByteResult>> assetFuture;
|
||||
auto featureTableJsonData =
|
||||
instancesBinary.subspan(headerLength, header.featureTableJsonByteLength);
|
||||
std::optional<I3dmContent> parsedJsonResult =
|
||||
parseI3dmJson(featureTableJsonData, convertedI3dm.gltfResult.errors);
|
||||
if (!parsedJsonResult) {
|
||||
finishEarly();
|
||||
}
|
||||
const I3dmContent& parsedContent = *parsedJsonResult;
|
||||
decodedInstances.rtcCenter = parsedContent.rtcCenter;
|
||||
decodedInstances.rotationENU = parsedContent.eastNorthUp;
|
||||
|
||||
auto featureTableBinaryData = instancesBinary.subspan(
|
||||
headerLength + header.featureTableJsonByteLength,
|
||||
header.featureTableBinaryByteLength);
|
||||
decodedInstances.positions.resize(
|
||||
parsedContent.instancesLength,
|
||||
glm::vec3(0.0f, 0.0f, 0.0f));
|
||||
if (parsedContent.position) {
|
||||
const auto* rawPosition = reinterpret_cast<const glm::vec3*>(
|
||||
featureTableBinaryData.data() + *parsedContent.position);
|
||||
for (unsigned i = 0; i < parsedContent.instancesLength; ++i) {
|
||||
decodedInstances.positions[i] += rawPosition[i];
|
||||
}
|
||||
auto binaryData = featureTableBinaryData.data();
|
||||
const uint32_t numInstances = parsedContent.instancesLength;
|
||||
decodedInstances.positions.resize(numInstances, glm::vec3(0.0f, 0.0f, 0.0f));
|
||||
if (parsedContent.position.has_value()) {
|
||||
gsl::span<const glm::vec3> rawPositions(reinterpret_cast<const glm::vec3*>(
|
||||
binaryData + *parsedContent.position),
|
||||
numInstances);
|
||||
decodedInstances.positions.assign(rawPositions.begin(), rawPositions.end());
|
||||
} else {
|
||||
const auto* rawQPosition = reinterpret_cast<const uint16_t(*)[3]>(
|
||||
featureTableBinaryData.data() + *parsedContent.positionQuantized);
|
||||
for (unsigned i = 0; i < parsedContent.instancesLength; ++i) {
|
||||
const auto* posQuantized = &rawQPosition[i];
|
||||
float position[3];
|
||||
for (unsigned j = 0; j < 3; ++j) {
|
||||
position[j] = static_cast<float>(
|
||||
(*posQuantized)[j] / 65535.0 *
|
||||
gsl::span<const uint16_t[3]> rawQPositions(reinterpret_cast<const uint16_t(*)[3]>(
|
||||
binaryData + *parsedContent.positionQuantized),
|
||||
numInstances);
|
||||
std::transform(
|
||||
rawQPositions.begin(),
|
||||
rawQPositions.end(),
|
||||
decodedInstances.positions.begin(),
|
||||
[&parsedContent](const auto&& posQuantized) {
|
||||
glm::vec3 position;
|
||||
for (unsigned j = 0; j < 3; ++j) {
|
||||
position[j] = static_cast<float>(
|
||||
posQuantized[j] / 65535.0 *
|
||||
(*parsedContent.quantizedVolumeScale)[j] +
|
||||
(*parsedContent.quantizedVolumeOffset)[j]);
|
||||
}
|
||||
decodedInstances.positions[i] +=
|
||||
glm::vec3(position[0], position[1], position[2]);
|
||||
}
|
||||
(*parsedContent.quantizedVolumeOffset)[j]);
|
||||
}
|
||||
return position;
|
||||
});
|
||||
}
|
||||
decodedInstances.rotations.resize(
|
||||
parsedContent.instancesLength,
|
||||
numInstances,
|
||||
glm::quat(1.0f, 0.0f, 0.0f, 0.0f));
|
||||
if (parsedContent.normalUp && parsedContent.normalRight) {
|
||||
const auto* rawUp = reinterpret_cast<const glm::vec3*>(
|
||||
featureTableBinaryData.data() + *parsedContent.normalUp);
|
||||
const auto* rawRight = reinterpret_cast<const glm::vec3*>(
|
||||
featureTableBinaryData.data() + *parsedContent.normalRight);
|
||||
for (unsigned i = 0; i < parsedContent.instancesLength; ++i) {
|
||||
decodedInstances.rotations[i] =
|
||||
rotationFromUpRight(rawUp[i], rawRight[i]);
|
||||
}
|
||||
} else if (parsedContent.normalUpOct32p && parsedContent.normalRightOct32p) {
|
||||
const auto* rawUpOct = reinterpret_cast<const uint16_t(*)[2]>(
|
||||
featureTableBinaryData.data() + *parsedContent.normalUpOct32p);
|
||||
const auto* rawRightOct = reinterpret_cast<const uint16_t(*)[2]>(
|
||||
featureTableBinaryData.data() + *parsedContent.normalRightOct32p);
|
||||
for (unsigned i = 0; i < parsedContent.instancesLength; ++i) {
|
||||
glm::vec3 dUp = decodeOct32P(rawUpOct[i]);
|
||||
glm::vec3 dRight = decodeOct32P(rawRightOct[i]);
|
||||
decodedInstances.rotations[i] = rotationFromUpRight(dUp, dRight);
|
||||
}
|
||||
if (parsedContent.normalUp.has_value() && parsedContent.normalRight.has_value()) {
|
||||
gsl::span<const glm::vec3> rawUp(reinterpret_cast<const glm::vec3*>(
|
||||
binaryData + *parsedContent.normalUp),
|
||||
numInstances);
|
||||
gsl::span<const glm::vec3> rawRight(reinterpret_cast<const glm::vec3*>(
|
||||
binaryData + *parsedContent.normalRight),
|
||||
numInstances);
|
||||
std::transform(rawUp.begin(),
|
||||
rawUp.end(),
|
||||
rawRight.begin(),
|
||||
decodedInstances.rotations.begin(),
|
||||
rotationFromUpRight);
|
||||
|
||||
} else if (parsedContent.normalUpOct32p.has_value() && parsedContent.normalRightOct32p.has_value()) {
|
||||
|
||||
gsl::span<const uint16_t[2]> rawUpOct(reinterpret_cast<const uint16_t(*)[2]>(
|
||||
binaryData + *parsedContent.normalUpOct32p),
|
||||
numInstances);
|
||||
gsl::span<const uint16_t[2]> rawRightOct(reinterpret_cast<const uint16_t(*)[2]>(
|
||||
binaryData + *parsedContent.normalRightOct32p),
|
||||
numInstances);
|
||||
std::transform(rawUpOct.begin(),
|
||||
rawUpOct.end(),
|
||||
rawRightOct.begin(),
|
||||
decodedInstances.rotations.begin(),
|
||||
[](const auto&& upOct, const auto&& rightOct) {
|
||||
return rotationFromUpRight(
|
||||
decodeOct32P(upOct),
|
||||
decodeOct32P(rightOct));
|
||||
});
|
||||
} else if (decodedInstances.rotationENU) {
|
||||
glm::dmat4 worldTransform = subprocessor.tileTransform;
|
||||
glm::dmat4 worldTransform = assetFetcher.tileTransform;
|
||||
if (decodedInstances.rtcCenter) {
|
||||
worldTransform = translate(worldTransform, *decodedInstances.rtcCenter);
|
||||
}
|
||||
|
|
@ -388,63 +444,72 @@ CesiumAsync::Future<ConvertResult> convertInstancesContent(
|
|||
decodedInstances.rotations[i] = tileFrameRot;
|
||||
}
|
||||
}
|
||||
decodedInstances.scales.resize(
|
||||
parsedContent.instancesLength,
|
||||
glm::vec3(1.0, 1.0, 1.0));
|
||||
if (parsedContent.scale) {
|
||||
const auto* rawScale = reinterpret_cast<const float*>(
|
||||
featureTableBinaryData.data() + *parsedContent.scale);
|
||||
for (unsigned i = 0; i < parsedContent.instancesLength; ++i) {
|
||||
decodedInstances.scales[i] =
|
||||
glm::vec3(rawScale[i], rawScale[i], rawScale[i]);
|
||||
}
|
||||
} else if (parsedContent.scaleNonUniform) {
|
||||
const auto* rawScaleNonUniform = reinterpret_cast<const glm::vec3*>(
|
||||
featureTableBinaryData.data() + *parsedContent.scaleNonUniform);
|
||||
for (unsigned i = 0; i < parsedContent.instancesLength; ++i) {
|
||||
decodedInstances.scales[i] = rawScaleNonUniform[i];
|
||||
}
|
||||
decodedInstances.scales.resize(numInstances, glm::vec3(1.0, 1.0, 1.0));
|
||||
if (parsedContent.scale.has_value()) {
|
||||
gsl::span<const float> rawScales(reinterpret_cast<const float*>(
|
||||
binaryData + *parsedContent.scale),
|
||||
numInstances);
|
||||
std::transform(rawScales.begin(),
|
||||
rawScales.end(),
|
||||
decodedInstances.scales.begin(),
|
||||
[](float scaleVal) {
|
||||
return glm::vec3(scaleVal);
|
||||
});
|
||||
}
|
||||
rebaseInstances(decodedInstances);
|
||||
if (parsedContent.scaleNonUniform.has_value()) {
|
||||
gsl::span<const glm::vec3> rawScalesNonUniform(
|
||||
reinterpret_cast<const glm::vec3*>(
|
||||
binaryData + *parsedContent.scaleNonUniform),
|
||||
numInstances);
|
||||
std::transform(
|
||||
decodedInstances.scales.begin(),
|
||||
decodedInstances.scales.end(),
|
||||
rawScalesNonUniform.begin(),
|
||||
decodedInstances.scales.begin(),
|
||||
[](auto&& scaleUniform, auto&& scaleNonUniform) {
|
||||
return scaleUniform * scaleNonUniform;
|
||||
});
|
||||
}
|
||||
repositionInstances(decodedInstances);
|
||||
ByteResult byteResult;
|
||||
if (header.gltfFormat == 0) {
|
||||
// Need to recursively read the glTF content.
|
||||
// Recursively fetch and read the glTF content.
|
||||
auto gltfUri = std::string(
|
||||
reinterpret_cast<const char*>(gltfData.data()),
|
||||
gltfData.size());
|
||||
return get(subprocessor, gltfUri)
|
||||
return assetFetcher.get(gltfUri)
|
||||
.thenImmediately(
|
||||
[options, subprocessor](ByteResult&& byteResult)
|
||||
[options, assetFetcher](ByteResult&& byteResult)
|
||||
-> CesiumAsync::Future<GltfConverterResult> {
|
||||
if (byteResult.errorList.hasErrors()) {
|
||||
GltfConverterResult errorResult;
|
||||
errorResult.errors.merge(byteResult.errorList);
|
||||
return subprocessor.asyncSystem.createResolvedFuture(
|
||||
return assetFetcher.asyncSystem.createResolvedFuture(
|
||||
std::move(errorResult));
|
||||
}
|
||||
return BinaryToGltfConverter::convert(
|
||||
byteResult.bytes,
|
||||
options,
|
||||
subprocessor);
|
||||
assetFetcher);
|
||||
})
|
||||
.thenImmediately([subResult = std::move(subResult)](
|
||||
.thenImmediately([convertedI3dm = std::move(convertedI3dm)](
|
||||
GltfConverterResult&& converterResult) mutable {
|
||||
if (converterResult.errors.hasErrors()) {
|
||||
subResult.gltfResult.errors.merge(converterResult.errors);
|
||||
convertedI3dm.gltfResult.errors.merge(converterResult.errors);
|
||||
} else {
|
||||
subResult.gltfResult = converterResult;
|
||||
convertedI3dm.gltfResult = converterResult;
|
||||
}
|
||||
return subResult;
|
||||
return convertedI3dm;
|
||||
});
|
||||
} else {
|
||||
return BinaryToGltfConverter::convert(gltfData, options, subprocessor)
|
||||
.thenImmediately([subResult = std::move(subResult)](
|
||||
return BinaryToGltfConverter::convert(gltfData, options, assetFetcher)
|
||||
.thenImmediately([convertedI3dm = std::move(convertedI3dm)](
|
||||
GltfConverterResult&& converterResult) mutable {
|
||||
if (converterResult.errors.hasErrors()) {
|
||||
return subResult;
|
||||
return convertedI3dm;
|
||||
}
|
||||
subResult.gltfResult = converterResult;
|
||||
return subResult;
|
||||
convertedI3dm.gltfResult = converterResult;
|
||||
return convertedI3dm;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -464,10 +529,10 @@ composeInstanceTransform(size_t i, const DecodedInstances& decodedInstances) {
|
|||
return result;
|
||||
}
|
||||
|
||||
std::vector<glm::dmat4> meshGpuInstances(
|
||||
GltfConverterResult& result,
|
||||
const ExtensionExtMeshGpuInstancing& gpuExt) {
|
||||
const Model& model = *result.model;
|
||||
std::vector<glm::dmat4> getMeshGpuInstancingTransforms(
|
||||
const Model& model,
|
||||
const ExtensionExtMeshGpuInstancing& gpuExt,
|
||||
CesiumUtility::ErrorList& errors) {
|
||||
std::vector<glm::dmat4> instances;
|
||||
if (gpuExt.attributes.empty()) {
|
||||
return instances;
|
||||
|
|
@ -480,7 +545,7 @@ std::vector<glm::dmat4> meshGpuInstances(
|
|||
return nullptr;
|
||||
};
|
||||
auto errorOut = [&](const std::string& errorMsg) {
|
||||
result.errors.emplaceError(errorMsg);
|
||||
errors.emplaceError(errorMsg);
|
||||
return instances;
|
||||
};
|
||||
|
||||
|
|
@ -582,7 +647,7 @@ void copyToBuffer(
|
|||
copyToBuffer(position, rotation, scale, &bufferData[i * totalStride]);
|
||||
}
|
||||
|
||||
void instantiateInstances(
|
||||
void instantiateGltfInstances(
|
||||
GltfConverterResult& result,
|
||||
const DecodedInstances& decodedInstances) {
|
||||
std::set<CesiumGltf::Node*> meshNodes;
|
||||
|
|
@ -617,7 +682,10 @@ void instantiateInstances(
|
|||
if (!gpuExt.attributes.empty()) {
|
||||
// The model already has instances! We will need to create the outer
|
||||
// product of these instances and those coming from i3dm.
|
||||
existingInstances = meshGpuInstances(result, gpuExt);
|
||||
existingInstances = getMeshGpuInstancingTransforms(
|
||||
*result.model,
|
||||
gpuExt,
|
||||
result.errors);
|
||||
if (numInstances * existingInstances.size() >
|
||||
std::numeric_limits<uint32_t>::max()) {
|
||||
result.errors.emplaceError(fmt::format(
|
||||
|
|
@ -627,6 +695,9 @@ void instantiateInstances(
|
|||
return;
|
||||
}
|
||||
}
|
||||
if (result.errors.hasErrors()) {
|
||||
return;
|
||||
}
|
||||
const uint32_t numNewInstances =
|
||||
static_cast<uint32_t>(numInstances * existingInstances.size());
|
||||
const size_t instanceDataSize = totalStride * numNewInstances;
|
||||
|
|
@ -687,7 +758,7 @@ void instantiateInstances(
|
|||
gpuExt.attributes["SCALE"] = scaleAccessorId;
|
||||
});
|
||||
if (decodedInstances.rtcCenter) {
|
||||
applyRTC(*result.model, *decodedInstances.rtcCenter);
|
||||
applyRtcToNodes(*result.model, *decodedInstances.rtcCenter);
|
||||
}
|
||||
instanceBuffer.byteLength =
|
||||
static_cast<int64_t>(instanceBuffer.cesium.data.size());
|
||||
|
|
@ -698,30 +769,30 @@ void instantiateInstances(
|
|||
CesiumAsync::Future<GltfConverterResult> I3dmToGltfConverter::convert(
|
||||
const gsl::span<const std::byte>& instancesBinary,
|
||||
const CesiumGltfReader::GltfReaderOptions& options,
|
||||
const ConverterSubprocessor& subProcessor) {
|
||||
const AssetFetcher& assetFetcher) {
|
||||
GltfConverterResult result;
|
||||
InstancesHeader header;
|
||||
I3dmHeader header;
|
||||
uint32_t headerLength = 0;
|
||||
parseInstancesHeader(instancesBinary, header, headerLength, result);
|
||||
parseI3dmHeader(instancesBinary, header, headerLength, result);
|
||||
if (result.errors) {
|
||||
return subProcessor.asyncSystem.createResolvedFuture(std::move(result));
|
||||
return assetFetcher.asyncSystem.createResolvedFuture(std::move(result));
|
||||
}
|
||||
DecodedInstances decodedInstances;
|
||||
return convertInstancesContent(
|
||||
return convertI3dmContent(
|
||||
instancesBinary,
|
||||
header,
|
||||
headerLength,
|
||||
options,
|
||||
subProcessor,
|
||||
assetFetcher,
|
||||
result)
|
||||
.thenImmediately([](ConvertResult&& convertResult) {
|
||||
if (convertResult.gltfResult.errors.hasErrors()) {
|
||||
return convertResult.gltfResult;
|
||||
.thenImmediately([](ConvertedI3dm&& convertedI3dm) {
|
||||
if (convertedI3dm.gltfResult.errors.hasErrors()) {
|
||||
return convertedI3dm.gltfResult;
|
||||
}
|
||||
instantiateInstances(
|
||||
convertResult.gltfResult,
|
||||
convertResult.decodedInstances);
|
||||
return convertResult.gltfResult;
|
||||
instantiateGltfInstances(
|
||||
convertedI3dm.gltfResult,
|
||||
convertedI3dm.decodedInstances);
|
||||
return convertedI3dm.gltfResult;
|
||||
});
|
||||
}
|
||||
} // namespace Cesium3DTilesContent
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
#include <Cesium3DTilesContent/LegacyUtilities.h>
|
||||
#include <CesiumAsync/IAssetResponse.h>
|
||||
#include <CesiumGltf/Accessor.h>
|
||||
#include <CesiumGltf/Buffer.h>
|
||||
#include <CesiumGltf/BufferView.h>
|
||||
|
|
@ -18,7 +17,7 @@ namespace Cesium3DTilesContent {
|
|||
namespace LegacyUtilities {
|
||||
using namespace CesiumGltf;
|
||||
|
||||
std::optional<uint32_t> parseOffset(
|
||||
std::optional<uint32_t> parseOffsetForSemantic(
|
||||
const rapidjson::Document& document,
|
||||
const char* semantic,
|
||||
CesiumUtility::ErrorList& errorList) {
|
||||
|
|
@ -78,19 +77,12 @@ parseArrayValueDVec3(const rapidjson::Document& document, const char* name) {
|
|||
return {};
|
||||
}
|
||||
|
||||
int32_t createBufferInGltf(Model& gltf) {
|
||||
int32_t createBufferInGltf(Model& gltf, std::vector<std::byte> buffer) {
|
||||
size_t bufferId = gltf.buffers.size();
|
||||
Buffer& gltfBuffer = gltf.buffers.emplace_back();
|
||||
gltfBuffer.byteLength = 0;
|
||||
return static_cast<int32_t>(bufferId);
|
||||
}
|
||||
|
||||
int32_t createBufferInGltf(Model& gltf, std::vector<std::byte>&& buffer) {
|
||||
int32_t bufferId = createBufferInGltf(gltf);
|
||||
Buffer& gltfBuffer = gltf.buffers[static_cast<uint32_t>(bufferId)];
|
||||
gltfBuffer.byteLength = static_cast<int32_t>(buffer.size());
|
||||
gltfBuffer.cesium.data = std::move(buffer);
|
||||
return bufferId;
|
||||
return static_cast<int32_t>(bufferId);
|
||||
}
|
||||
|
||||
int32_t createBufferViewInGltf(
|
||||
|
|
@ -126,7 +118,7 @@ int32_t createAccessorInGltf(
|
|||
return static_cast<int32_t>(accessorId);
|
||||
}
|
||||
|
||||
void applyRTC(Model& gltf, const glm::dvec3& rtc) {
|
||||
void applyRtcToNodes(Model& gltf, const glm::dvec3& rtc) {
|
||||
using namespace CesiumGltfContent;
|
||||
auto upToZ = GltfUtilities::applyGltfUpAxisTransform(gltf, glm::dmat4x4(1.0));
|
||||
auto rtcTransform = inverse(upToZ);
|
||||
|
|
@ -142,40 +134,4 @@ void applyRTC(Model& gltf, const glm::dvec3& rtc) {
|
|||
}
|
||||
|
||||
} // namespace LegacyUtilities
|
||||
|
||||
CesiumAsync::Future<ByteResult>
|
||||
get(const ConverterSubprocessor& subprocessor, const std::string& relativeUrl) {
|
||||
auto resolvedUrl =
|
||||
CesiumUtility::Uri::resolve(subprocessor.baseUrl, relativeUrl);
|
||||
return subprocessor.pAssetAccessor
|
||||
->get(subprocessor.asyncSystem, resolvedUrl, subprocessor.requestHeaders)
|
||||
.thenImmediately(
|
||||
[asyncSystem = subprocessor.asyncSystem](
|
||||
std::shared_ptr<CesiumAsync::IAssetRequest>&& pCompletedRequest) {
|
||||
const CesiumAsync::IAssetResponse* pResponse =
|
||||
pCompletedRequest->response();
|
||||
ByteResult byteResult;
|
||||
const auto& url = pCompletedRequest->url();
|
||||
if (!pResponse) {
|
||||
byteResult.errorList.emplaceError(fmt::format(
|
||||
"Did not receive a valid response for asset {}",
|
||||
url));
|
||||
return asyncSystem.createResolvedFuture(std::move(byteResult));
|
||||
}
|
||||
uint16_t statusCode = pResponse->statusCode();
|
||||
if (statusCode != 0 && (statusCode < 200 || statusCode >= 300)) {
|
||||
byteResult.errorList.emplaceError(fmt::format(
|
||||
"Received status code {} for asset {}",
|
||||
statusCode,
|
||||
url));
|
||||
return asyncSystem.createResolvedFuture(std::move(byteResult));
|
||||
}
|
||||
gsl::span<const std::byte> asset = pResponse->data();
|
||||
std::copy(
|
||||
asset.begin(),
|
||||
asset.end(),
|
||||
std::back_inserter(byteResult.bytes));
|
||||
return asyncSystem.createResolvedFuture(std::move(byteResult));
|
||||
});
|
||||
}
|
||||
} // namespace Cesium3DTilesContent
|
||||
|
|
|
|||
|
|
@ -1633,7 +1633,7 @@ void convertPntsContentToGltf(
|
|||
CesiumAsync::Future<GltfConverterResult> PntsToGltfConverter::convert(
|
||||
const gsl::span<const std::byte>& pntsBinary,
|
||||
const CesiumGltfReader::GltfReaderOptions& /*options*/,
|
||||
const ConverterSubprocessor& subprocessor) {
|
||||
const AssetFetcher& assetFetcher) {
|
||||
GltfConverterResult result;
|
||||
PntsHeader header;
|
||||
uint32_t headerLength = 0;
|
||||
|
|
@ -1642,6 +1642,6 @@ CesiumAsync::Future<GltfConverterResult> PntsToGltfConverter::convert(
|
|||
convertPntsContentToGltf(pntsBinary, header, headerLength, result);
|
||||
}
|
||||
|
||||
return subprocessor.asyncSystem.createResolvedFuture(std::move(result));
|
||||
return assetFetcher.asyncSystem.createResolvedFuture(std::move(result));
|
||||
}
|
||||
} // namespace Cesium3DTilesContent
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@ namespace Cesium3DTilesContent {
|
|||
CesiumAsync::AsyncSystem ConvertTileToGltf::asyncSystem(
|
||||
std::make_shared<CesiumNativeTests::SimpleTaskProcessor>());
|
||||
|
||||
ConverterSubprocessor
|
||||
ConvertTileToGltf::makeSubprocessor(const std::string& baseUrl) {
|
||||
AssetFetcher
|
||||
ConvertTileToGltf::makeAssetFetcher(const std::string& baseUrl) {
|
||||
auto fileAccessor = std::make_shared<CesiumNativeTests::FileAccessor>();
|
||||
std::vector<CesiumAsync::IAssetAccessor::THeader> requestHeaders;
|
||||
return ConverterSubprocessor(
|
||||
return AssetFetcher(
|
||||
asyncSystem,
|
||||
fileAccessor,
|
||||
baseUrl,
|
||||
|
|
@ -24,18 +24,18 @@ ConvertTileToGltf::makeSubprocessor(const std::string& baseUrl) {
|
|||
GltfConverterResult ConvertTileToGltf::fromB3dm(
|
||||
const std::filesystem::path& filePath,
|
||||
const CesiumGltfReader::GltfReaderOptions& options) {
|
||||
ConverterSubprocessor subprocessor = makeSubprocessor("");
|
||||
AssetFetcher assetFetcher = makeAssetFetcher("");
|
||||
auto bytes = readFile(filePath);
|
||||
auto future = B3dmToGltfConverter::convert(bytes, options, subprocessor);
|
||||
auto future = B3dmToGltfConverter::convert(bytes, options, assetFetcher);
|
||||
return future.wait();
|
||||
}
|
||||
|
||||
GltfConverterResult ConvertTileToGltf::fromPnts(
|
||||
const std::filesystem::path& filePath,
|
||||
const CesiumGltfReader::GltfReaderOptions& options) {
|
||||
ConverterSubprocessor subprocessor = makeSubprocessor("");
|
||||
AssetFetcher assetFetcher = makeAssetFetcher("");
|
||||
auto bytes = readFile(filePath);
|
||||
auto future = PntsToGltfConverter::convert(bytes, options, subprocessor);
|
||||
auto future = PntsToGltfConverter::convert(bytes, options, assetFetcher);
|
||||
return future.wait();
|
||||
}
|
||||
} // namespace Cesium3DTilesContent
|
||||
|
|
|
|||
|
|
@ -152,13 +152,13 @@ CesiumAsync::Future<TileLoadResult> requestTileContent(
|
|||
CesiumGltfReader::GltfReaderOptions gltfOptions;
|
||||
gltfOptions.ktx2TranscodeTargets = ktx2TranscodeTargets;
|
||||
gltfOptions.applyTextureTransform = applyTextureTransform;
|
||||
ConverterSubprocessor subprocessor{
|
||||
AssetFetcher assetFetcher{
|
||||
asyncSystem,
|
||||
pAssetAccessor,
|
||||
tileUrl,
|
||||
tileTransform,
|
||||
requestHeaders};
|
||||
return converter(responseData, gltfOptions, subprocessor)
|
||||
return converter(responseData, gltfOptions, assetFetcher)
|
||||
.thenImmediately([pLogger, tileUrl, pCompletedRequest](
|
||||
GltfConverterResult&& result) {
|
||||
// Report any errors if there are any
|
||||
|
|
|
|||
|
|
@ -164,13 +164,13 @@ CesiumAsync::Future<TileLoadResult> requestTileContent(
|
|||
CesiumGltfReader::GltfReaderOptions gltfOptions;
|
||||
gltfOptions.ktx2TranscodeTargets = ktx2TranscodeTargets;
|
||||
gltfOptions.applyTextureTransform = applyTextureTransform;
|
||||
ConverterSubprocessor subprocessor{
|
||||
AssetFetcher assetFetcher{
|
||||
asyncSystem,
|
||||
pAssetAccessor,
|
||||
tileUrl,
|
||||
tileTransform,
|
||||
requestHeaders};
|
||||
return converter(responseData, gltfOptions, subprocessor)
|
||||
return converter(responseData, gltfOptions, assetFetcher)
|
||||
.thenImmediately([pLogger, tileUrl, pCompletedRequest](
|
||||
GltfConverterResult&& result) {
|
||||
// Report any errors if there are any
|
||||
|
|
|
|||
|
|
@ -903,7 +903,7 @@ TilesetJsonLoader::loadTileContent(const TileLoadInput& loadInput) {
|
|||
|
||||
if (converter) {
|
||||
// Convert to gltf
|
||||
ConverterSubprocessor subprocessor{
|
||||
AssetFetcher assetFetcher{
|
||||
asyncSystem,
|
||||
pAssetAccessor,
|
||||
tileUrl,
|
||||
|
|
@ -914,7 +914,7 @@ TilesetJsonLoader::loadTileContent(const TileLoadInput& loadInput) {
|
|||
contentOptions.ktx2TranscodeTargets;
|
||||
gltfOptions.applyTextureTransform =
|
||||
contentOptions.applyTextureTransform;
|
||||
return converter(responseData, gltfOptions, subprocessor)
|
||||
return converter(responseData, gltfOptions, assetFetcher)
|
||||
.thenImmediately([pLogger, upAxis, tileUrl, pCompletedRequest](
|
||||
GltfConverterResult&& result) {
|
||||
logTileLoadResult(pLogger, tileUrl, result.errors);
|
||||
|
|
|
|||
|
|
@ -309,8 +309,8 @@ struct TexCoordFromAccessor {
|
|||
};
|
||||
|
||||
/**
|
||||
* Type definition for quaternion accessors, as defined for ExtMeshGpuInstancing
|
||||
* and animation samplers.
|
||||
* Type definition for quaternion accessors, as used in ExtMeshGpuInstancing
|
||||
* rotations and animation samplers.
|
||||
*/
|
||||
typedef std::variant<
|
||||
AccessorView<AccessorTypes::VEC4<uint8_t>>,
|
||||
|
|
|
|||
|
|
@ -19,8 +19,7 @@ struct CESIUMGLTF_API Model : public ModelSpec {
|
|||
* elements that were originally in it _plus_ all of the elements
|
||||
* that were in `rhs`. Element indices are updated accordingly.
|
||||
* However, element indices in {@link ExtensibleObject::extras}, if any,
|
||||
* are _not_ updated. Most extensions aren't supported either, except for
|
||||
* KHR_draco_mesh_compression and EXT_mesh_gpu_instancing.
|
||||
* are _not_ updated.
|
||||
*
|
||||
* @param rhs The model to merge into this one.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -299,12 +299,12 @@ GltfUtilities::parseGltfCopyright(const CesiumGltf::Model& gltf) {
|
|||
}
|
||||
|
||||
// Copy the data to the destination and keep track of where we put it.
|
||||
// Align each bufferView to a 4-byte boundary.
|
||||
// Align each bufferView to an 8-byte boundary.
|
||||
size_t start = destination.cesium.data.size();
|
||||
|
||||
size_t alignmentRemainder = start % 4;
|
||||
size_t alignmentRemainder = start % 8;
|
||||
if (alignmentRemainder != 0) {
|
||||
start += 4 - alignmentRemainder;
|
||||
start += 8 - alignmentRemainder;
|
||||
}
|
||||
|
||||
destination.cesium.data.resize(start + source.cesium.data.size());
|
||||
|
|
@ -628,6 +628,18 @@ void deleteBufferRange(
|
|||
|
||||
int64_t bytesToRemove = end - start;
|
||||
|
||||
// In order to ensure that we can't disrupt glTF's alignment requirements,
|
||||
// only remove multiples of 8 bytes from within the buffer (removing any
|
||||
// number of bytes from the end is fine).
|
||||
if (end < pBuffer->byteLength) {
|
||||
// Round down to the nearest multiple of 8 by clearing the low three bits.
|
||||
bytesToRemove = bytesToRemove & ~0b111;
|
||||
if (bytesToRemove == 0)
|
||||
return;
|
||||
|
||||
end = start + bytesToRemove;
|
||||
}
|
||||
|
||||
// Adjust bufferView offets for the removed bytes.
|
||||
for (BufferView& bufferView : gltf.bufferViews) {
|
||||
if (bufferView.buffer != bufferIndex)
|
||||
|
|
|
|||
|
|
@ -383,12 +383,12 @@ TEST_CASE("GltfUtilities::compactBuffers") {
|
|||
|
||||
GltfUtilities::compactBuffers(m);
|
||||
|
||||
CHECK(buffer.byteLength == 113);
|
||||
REQUIRE(buffer.cesium.data.size() == 113);
|
||||
CHECK(bv.byteOffset == 0);
|
||||
CHECK(buffer.byteLength == 123 - 8);
|
||||
REQUIRE(buffer.cesium.data.size() == 123 - 8);
|
||||
CHECK(bv.byteOffset == 2);
|
||||
|
||||
for (size_t i = 0; i < buffer.cesium.data.size(); ++i) {
|
||||
CHECK(buffer.cesium.data[i] == std::byte(i + 10));
|
||||
for (size_t i = size_t(bv.byteOffset); i < buffer.cesium.data.size(); ++i) {
|
||||
CHECK(buffer.cesium.data[i] == std::byte(i + 8));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -400,8 +400,9 @@ TEST_CASE("GltfUtilities::compactBuffers") {
|
|||
|
||||
GltfUtilities::compactBuffers(m);
|
||||
|
||||
CHECK(buffer.byteLength == 113);
|
||||
REQUIRE(buffer.cesium.data.size() == 113);
|
||||
// Any number of bytes can be removed from the end (no alignment impact)
|
||||
CHECK(buffer.byteLength == 123 - 10);
|
||||
REQUIRE(buffer.cesium.data.size() == 123 - 10);
|
||||
|
||||
for (size_t i = 0; i < buffer.cesium.data.size(); ++i) {
|
||||
CHECK(buffer.cesium.data[i] == std::byte(i));
|
||||
|
|
@ -416,12 +417,35 @@ TEST_CASE("GltfUtilities::compactBuffers") {
|
|||
|
||||
GltfUtilities::compactBuffers(m);
|
||||
|
||||
CHECK(buffer.byteLength == 103);
|
||||
REQUIRE(buffer.cesium.data.size() == 103);
|
||||
CHECK(bv.byteOffset == 0);
|
||||
CHECK(buffer.byteLength == 123 - 8 - 10);
|
||||
REQUIRE(buffer.cesium.data.size() == 123 - 8 - 10);
|
||||
CHECK(bv.byteOffset == 2);
|
||||
|
||||
for (size_t i = 2; i < buffer.cesium.data.size(); ++i) {
|
||||
CHECK(buffer.cesium.data[i] == std::byte(i + 8));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("does not remove gaps less than 8 bytes") {
|
||||
BufferView& bv1 = m.bufferViews.emplace_back();
|
||||
bv1.buffer = 0;
|
||||
bv1.byteOffset = 1;
|
||||
bv1.byteLength = 99;
|
||||
|
||||
BufferView& bv2 = m.bufferViews.emplace_back();
|
||||
bv2.buffer = 0;
|
||||
bv2.byteOffset = 105;
|
||||
bv2.byteLength = 10;
|
||||
|
||||
GltfUtilities::compactBuffers(m);
|
||||
|
||||
CHECK(buffer.byteLength == 115);
|
||||
REQUIRE(buffer.cesium.data.size() == 115);
|
||||
CHECK(m.bufferViews[0].byteOffset == 1);
|
||||
CHECK(m.bufferViews[1].byteOffset == 105);
|
||||
|
||||
for (size_t i = 0; i < buffer.cesium.data.size(); ++i) {
|
||||
CHECK(buffer.cesium.data[i] == std::byte(i + 10));
|
||||
CHECK(buffer.cesium.data[i] == std::byte(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ public:
|
|||
|
||||
CesiumAsync::Future<std::shared_ptr<CesiumAsync::IAssetRequest>> request(
|
||||
const CesiumAsync::AsyncSystem& asyncSystem,
|
||||
const std::string& /* verb */,
|
||||
const std::string& verb,
|
||||
const std::string& url,
|
||||
const std::vector<THeader>& headers,
|
||||
const gsl::span<const std::byte>&) override;
|
||||
|
|
|
|||
Loading…
Reference in New Issue