From 3ba003a53945c47d8720e9063c85455139ef96d2 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Sun, 7 Apr 2024 13:25:23 +0200 Subject: [PATCH 01/32] Make CESIUM_DEBUG_PREFIX and CESIUM_RELEASE_PREFIX cache variables This allows the developer to override them usefully in situations where they need to be set e.g., make a debug library masquerade as release. Also allow CESIUM_DEBUG_PREFIX to be empty. --- CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a44ad99f..746a547b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,8 +36,8 @@ if (NOT DEFINED GLOB_USE_CONFIGURE_DEPENDS) ) endif() -set(CESIUM_DEBUG_POSTFIX "d") -set(CESIUM_RELEASE_POSTFIX "") +set(CESIUM_DEBUG_POSTFIX "d" CACHE STRING "debug postfix for cesium native") +set(CESIUM_RELEASE_POSTFIX "" CACHE STRING "release postfix for cesium native") set(CMAKE_DEBUG_POSTFIX ${CESIUM_DEBUG_POSTFIX}) set(CMAKE_RELEASE_POSTFIX ${CESIUM_RELEASE_POSTFIX}) @@ -132,8 +132,8 @@ endfunction() add_subdirectory(extern EXCLUDE_FROM_ALL) # These libraries override the debug postfix, so re-override it. -set_target_properties(spdlog PROPERTIES DEBUG_POSTFIX ${CESIUM_DEBUG_POSTFIX}) -set_target_properties(tinyxml2 PROPERTIES DEBUG_POSTFIX ${CESIUM_DEBUG_POSTFIX}) +set_target_properties(spdlog PROPERTIES DEBUG_POSTFIX "${CESIUM_DEBUG_POSTFIX}") +set_target_properties(tinyxml2 PROPERTIES DEBUG_POSTFIX "${CESIUM_DEBUG_POSTFIX}") # Public Targets add_subdirectory(CesiumUtility) From ff427ace80ecd83a58c2c79194c8ea935d6cd226 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Sun, 7 Apr 2024 13:31:14 +0200 Subject: [PATCH 02/32] Fix CesiumGltf::Model::merge to handle EXT_mesh_gpu_instancing This is all very ad hoc and there will be more extensions that will have to be handled in the future. This function is currently only used by Cmpt parsing code, but one could imagine it being generally useful. --- CesiumGltf/include/CesiumGltf/Model.h | 3 ++- CesiumGltf/src/Model.cpp | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CesiumGltf/include/CesiumGltf/Model.h b/CesiumGltf/include/CesiumGltf/Model.h index d88694f14..ee96216dd 100644 --- a/CesiumGltf/include/CesiumGltf/Model.h +++ b/CesiumGltf/include/CesiumGltf/Model.h @@ -18,7 +18,8 @@ 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. + * are _not_ updated. Most extensions aren't supported either, except for + * KHR_draco_mesh_compression and EXT_mesh_gpu_instancing. * * @param rhs The model to merge into this one. */ diff --git a/CesiumGltf/src/Model.cpp b/CesiumGltf/src/Model.cpp index 926e19542..c1c625ebe 100644 --- a/CesiumGltf/src/Model.cpp +++ b/CesiumGltf/src/Model.cpp @@ -1,6 +1,7 @@ #include "CesiumGltf/Model.h" #include "CesiumGltf/AccessorView.h" +#include "CesiumGltf/ExtensionExtMeshGpuInstancing.h" #include "CesiumGltf/ExtensionKhrDracoMeshCompression.h" #include @@ -131,6 +132,14 @@ void Model::merge(Model&& rhs) { updateIndex(node.camera, firstCamera); updateIndex(node.skin, firstSkin); updateIndex(node.mesh, firstMesh); + if (node.mesh >= 0) { + if (auto* meshGpuInstancing = + node.getExtension()) { + for (auto& attrPair : meshGpuInstancing->attributes) { + updateIndex(attrPair.second, firstAccessor); + } + } + } for (auto& nodeIndex : node.children) { updateIndex(nodeIndex, firstNode); From dd563d5c72d64d71055cf4ba5d6213def57d602b Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Mon, 8 Apr 2024 18:43:35 +0200 Subject: [PATCH 03/32] Add support for I3dm 3D Tile instance files Initial commit of the main body of new code. A lot was taken or inspired from PntsToGltfConverter, which should now be refactored to use the new common functions. ENU rotations are not supported yet. --- CHANGES.md | 6 + .../B3dmToGltfConverter.h | 5 +- .../BinaryToGltfConverter.h | 5 +- .../CmptToGltfConverter.h | 5 +- .../GltfConverterResult.h | 5 + .../Cesium3DTilesContent/GltfConverters.h | 33 +- .../I3dmToGltfConverter.h | 22 + .../Cesium3DTilesContent/LegacyUtilities.h | 98 ++++ .../PntsToGltfConverter.h | 5 +- .../src/B3dmToGltfConverter.cpp | 5 +- .../src/BinaryToGltfConverter.cpp | 3 +- .../src/CmptToGltfConverter.cpp | 6 +- Cesium3DTilesContent/src/GltfConverters.cpp | 12 +- .../src/I3dmToGltfConverter.cpp | 430 ++++++++++++++++++ Cesium3DTilesContent/src/LegacyUtilities.cpp | 149 ++++++ .../src/PntsToGltfConverter.cpp | 3 +- .../src/registerAllTileContentTypes.cpp | 2 + .../src/ImplicitOctreeLoader.cpp | 3 +- .../src/ImplicitQuadtreeLoader.cpp | 3 +- .../src/TilesetJsonLoader.cpp | 17 +- 20 files changed, 793 insertions(+), 24 deletions(-) create mode 100644 Cesium3DTilesContent/include/Cesium3DTilesContent/I3dmToGltfConverter.h create mode 100644 Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h create mode 100644 Cesium3DTilesContent/src/I3dmToGltfConverter.cpp create mode 100644 Cesium3DTilesContent/src/LegacyUtilities.cpp diff --git a/CHANGES.md b/CHANGES.md index 4d70588f4..0ffbcbeea 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Change Log +### v0.34.0 + +##### Additions :tada: + +- Added support for I3dm 3D Tile content files. + ### v0.33.0 - 2024-03-01 ##### Breaking Changes :mega: diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/B3dmToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/B3dmToGltfConverter.h index a13e643c4..9cd99b028 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/B3dmToGltfConverter.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/B3dmToGltfConverter.h @@ -10,9 +10,12 @@ #include namespace Cesium3DTilesContent { +struct ConverterSubprocessor; + struct B3dmToGltfConverter { static GltfConverterResult convert( const gsl::span& b3dmBinary, - const CesiumGltfReader::GltfReaderOptions& options); + const CesiumGltfReader::GltfReaderOptions& options, + ConverterSubprocessor*); }; } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h index 910f3c305..6a2b499d1 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h @@ -8,11 +8,14 @@ #include namespace Cesium3DTilesContent { +struct ConverterSubprocessor; + struct BinaryToGltfConverter { public: static GltfConverterResult convert( const gsl::span& gltfBinary, - const CesiumGltfReader::GltfReaderOptions& options); + const CesiumGltfReader::GltfReaderOptions& options, + ConverterSubprocessor* subProcessor); private: static CesiumGltfReader::GltfReader _gltfReader; diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/CmptToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/CmptToGltfConverter.h index ad7280a32..50f2cccae 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/CmptToGltfConverter.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/CmptToGltfConverter.h @@ -8,9 +8,12 @@ #include namespace Cesium3DTilesContent { +struct ConverterSubprocessor; + struct CmptToGltfConverter { static GltfConverterResult convert( const gsl::span& cmptBinary, - const CesiumGltfReader::GltfReaderOptions& options); + const CesiumGltfReader::GltfReaderOptions& options, + ConverterSubprocessor*); }; } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverterResult.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverterResult.h index 54fd31114..b32de5244 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverterResult.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverterResult.h @@ -5,7 +5,12 @@ #include #include +#include +#include + #include +#include +#include namespace Cesium3DTilesContent { /** diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h index bc07748d1..3a1f71346 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h @@ -12,6 +12,30 @@ #include namespace Cesium3DTilesContent { + +/** + * Data required to make a recursive request to fetch an asset, mostly for the + * benefit of I3dm files. + */ +struct CESIUM3DTILESCONTENT_API ConverterSubprocessor { + ConverterSubprocessor( + const CesiumAsync::AsyncSystem& asyncSystem_, + const std::shared_ptr& pAssetAccessor_, + const std::string& baseUrl_, + const glm::dmat4 tileTransform_, + const std::vector& requestHeaders_) + : asyncSystem(asyncSystem_), + pAssetAccessor(pAssetAccessor_), + baseUrl(baseUrl_), + tileTransform(tileTransform_), + requestHeaders(requestHeaders_) {} + const CesiumAsync::AsyncSystem& asyncSystem; + const std::shared_ptr pAssetAccessor; + const std::string baseUrl; + glm::dmat4 tileTransform; + const std::vector& requestHeaders; +}; + /** * @brief Creates {@link GltfConverterResult} objects from a * a binary content. @@ -34,7 +58,8 @@ class CESIUM3DTILESCONTENT_API GltfConverters { */ using ConverterFunction = GltfConverterResult (*)( const gsl::span& content, - const CesiumGltfReader::GltfReaderOptions& options); + const CesiumGltfReader::GltfReaderOptions& options, + ConverterSubprocessor* subprocessor); /** * @brief Register the given function for the given magic header. @@ -123,7 +148,8 @@ class CESIUM3DTILESCONTENT_API GltfConverters { static GltfConverterResult convert( const std::string& filePath, const gsl::span& content, - const CesiumGltfReader::GltfReaderOptions& options); + const CesiumGltfReader::GltfReaderOptions& options, + ConverterSubprocessor* subprocessor); /** * @brief Creates the {@link GltfConverterResult} from the given @@ -147,7 +173,8 @@ class CESIUM3DTILESCONTENT_API GltfConverters { */ static GltfConverterResult convert( const gsl::span& content, - const CesiumGltfReader::GltfReaderOptions& options); + const CesiumGltfReader::GltfReaderOptions& options, + ConverterSubprocessor* subprocessor); private: static std::string toLowerCase(const std::string_view& str); diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/I3dmToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/I3dmToGltfConverter.h new file mode 100644 index 000000000..6526f6b15 --- /dev/null +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/I3dmToGltfConverter.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#include +#include + +#include +#include + +#include + +namespace Cesium3DTilesContent { +struct ConverterSubprocessor; + +struct I3dmToGltfConverter { + static GltfConverterResult convert( + const gsl::span& instancesBinary, + const CesiumGltfReader::GltfReaderOptions& options, + ConverterSubprocessor* subprocessor); +}; +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h new file mode 100644 index 000000000..64ccf360f --- /dev/null +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h @@ -0,0 +1,98 @@ +#pragma once + +#include +#include + +#include +#include + +#include + +#include + +#include +#include +#include + +namespace CesiumGltf { +class Model; +class Buffer; +} + +namespace Cesium3DTilesContent { + +struct ByteResult { + std::vector bytes; + CesiumUtility::ErrorList errorList; +}; + +CesiumAsync::Future get(const ConverterSubprocessor& subprocessor, const std::string& relativeUrl); + +namespace LegacyUtilities { +std::optional parseOffset(const rapidjson::Document& document, const char* semantic, CesiumUtility::ErrorList& errorList); + +typedef bool (rapidjson::Value::*ValuePredicate)() const; + +template bool isValue(const rapidjson::Value& value); +template T getValue(const rapidjson::Value& value); + +template +std::optional getOpt(const rapidjson::Value& value) { + if (isValue(value)) { + return std::make_optional(getValue(value)); + } + return {}; +} + +template +std::optional getValue(const rapidjson::Document& document, const char *semantic) { + const auto valueIt = document.FindMember(semantic); + if (valueIt == document.MemberEnd() || !isValue(valueIt->value)) { + return {}; + } + return std::make_optional(getValue(valueIt->value)); +} + +template<> +inline bool isValue(const rapidjson::Value& value) { + return value.IsBool(); +} + +template<> +inline bool getValue(const rapidjson::Value& value) { + return value.GetBool(); +} + +template<> +inline bool isValue(const rapidjson::Value& value) { + return value.IsUint(); +} + +template<> +inline uint32_t getValue(const rapidjson::Value& value) { + return value.GetUint(); +} + +bool validateJsonArrayValues(const rapidjson::Value& arrayValue, uint32_t expectedLength, ValuePredicate predicate); + +std::optional parseArrayValueVec3(const rapidjson::Value& arrayValue); + +std::optional parseArrayValueVec3(const rapidjson::Document& document, const char *name); + +int32_t createBufferInGltf(CesiumGltf::Model& gltf); +int32_t createBufferInGltf(CesiumGltf::Model& gltf, std::vector&& buffer); + +int32_t createBufferViewInGltf( + CesiumGltf::Model& gltf, + const int32_t bufferId, + const int64_t byteLength, + const int64_t byteStride); + +int32_t createAccessorInGltf( + CesiumGltf::Model& gltf, + const int32_t bufferViewId, + const int32_t componentType, + const int64_t count, + const std::string type); +} +} diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/PntsToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/PntsToGltfConverter.h index 48593ca00..963071cd9 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/PntsToGltfConverter.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/PntsToGltfConverter.h @@ -10,9 +10,12 @@ #include namespace Cesium3DTilesContent { +struct ConverterSubprocessor; + struct PntsToGltfConverter { static GltfConverterResult convert( const gsl::span& pntsBinary, - const CesiumGltfReader::GltfReaderOptions& options); + const CesiumGltfReader::GltfReaderOptions& options, + ConverterSubprocessor*); }; } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/B3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/B3dmToGltfConverter.cpp index 629679633..99e2620ed 100644 --- a/Cesium3DTilesContent/src/B3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/B3dmToGltfConverter.cpp @@ -136,7 +136,7 @@ void convertB3dmContentToGltf( b3dmBinary.subspan(glbStart, glbEnd - glbStart); GltfConverterResult binToGltfResult = - BinaryToGltfConverter::convert(glbData, options); + BinaryToGltfConverter::convert(glbData, options, nullptr); result.model = std::move(binToGltfResult.model); result.errors.merge(std::move(binToGltfResult.errors)); @@ -231,7 +231,8 @@ void convertB3dmMetadataToGltfStructuralMetadata( GltfConverterResult B3dmToGltfConverter::convert( const gsl::span& b3dmBinary, - const CesiumGltfReader::GltfReaderOptions& options) { + const CesiumGltfReader::GltfReaderOptions& options, + ConverterSubprocessor*) { GltfConverterResult result; B3dmHeader header; uint32_t headerLength = 0; diff --git a/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp b/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp index d6420c89b..8c96973f3 100644 --- a/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp @@ -5,7 +5,8 @@ CesiumGltfReader::GltfReader BinaryToGltfConverter::_gltfReader; GltfConverterResult BinaryToGltfConverter::convert( const gsl::span& gltfBinary, - const CesiumGltfReader::GltfReaderOptions& options) { + const CesiumGltfReader::GltfReaderOptions& options, + ConverterSubprocessor*) { CesiumGltfReader::GltfReaderResult loadedGltf = _gltfReader.readGltf(gltfBinary, options); diff --git a/Cesium3DTilesContent/src/CmptToGltfConverter.cpp b/Cesium3DTilesContent/src/CmptToGltfConverter.cpp index 5676e42e4..ba287397e 100644 --- a/Cesium3DTilesContent/src/CmptToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/CmptToGltfConverter.cpp @@ -24,7 +24,8 @@ static_assert(sizeof(InnerHeader) == 12); GltfConverterResult CmptToGltfConverter::convert( const gsl::span& cmptBinary, - const CesiumGltfReader::GltfReaderOptions& options) { + const CesiumGltfReader::GltfReaderOptions& options, + ConverterSubprocessor* subProcessor) { GltfConverterResult result; if (cmptBinary.size() < sizeof(CmptHeader)) { result.errors.emplaceWarning("Composite tile must be at least 16 bytes."); @@ -78,7 +79,8 @@ GltfConverterResult CmptToGltfConverter::convert( pos += pInner->byteLength; - innerTiles.emplace_back(GltfConverters::convert(innerData, options)); + innerTiles.emplace_back( + GltfConverters::convert(innerData, options, subProcessor)); } uint32_t tilesLength = pHeader->tilesLength; diff --git a/Cesium3DTilesContent/src/GltfConverters.cpp b/Cesium3DTilesContent/src/GltfConverters.cpp index bd0b9b9cb..0617c33c2 100644 --- a/Cesium3DTilesContent/src/GltfConverters.cpp +++ b/Cesium3DTilesContent/src/GltfConverters.cpp @@ -40,17 +40,18 @@ GltfConverters::getConverterByMagic(const gsl::span& content) { GltfConverterResult GltfConverters::convert( const std::string& filePath, const gsl::span& content, - const CesiumGltfReader::GltfReaderOptions& options) { + const CesiumGltfReader::GltfReaderOptions& options, + ConverterSubprocessor* subprocessor) { std::string magic; auto converterFun = getConverterByMagic(content, magic); if (converterFun) { - return converterFun(content, options); + return converterFun(content, options, subprocessor); } std::string fileExtension; converterFun = getConverterByFileExtension(filePath, fileExtension); if (converterFun) { - return converterFun(content, options); + return converterFun(content, options, subprocessor); } ErrorList errors; @@ -65,11 +66,12 @@ GltfConverterResult GltfConverters::convert( GltfConverterResult GltfConverters::convert( const gsl::span& content, - const CesiumGltfReader::GltfReaderOptions& options) { + const CesiumGltfReader::GltfReaderOptions& options, + ConverterSubprocessor* subprocessor) { std::string magic; auto converter = getConverterByMagic(content, magic); if (converter) { - return converter(content, options); + return converter(content, options, subprocessor); } ErrorList errors; diff --git a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp new file mode 100644 index 000000000..2075f6477 --- /dev/null +++ b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp @@ -0,0 +1,430 @@ +// Heavily inspired by PntsToGltfConverter.cpp + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +using namespace CesiumGltf; + +namespace Cesium3DTilesContent { +using namespace LegacyUtilities; + +namespace { +struct InstancesHeader { + unsigned char magic[4]; + uint32_t version; + uint32_t byteLength; + uint32_t featureTableJsonByteLength; + uint32_t featureTableBinaryByteLength; + uint32_t batchTableJsonByteLength; + uint32_t batchTableBinaryByteLength; + uint32_t gltfFormat; +}; + +struct DecodedInstances { + std::vector positions; + std::vector rotations; + std::vector scales; + std::string gltfUri; + bool rotationENU; +}; + + +void parseInstancesHeader( + const gsl::span& instancesBinary, + InstancesHeader& header, + uint32_t& headerLength, + GltfConverterResult& result) { + if (instancesBinary.size() < sizeof(InstancesHeader)) { + result.errors.emplaceError("The I3DM is invalid because it is too small to " + "include a I3DM header."); + return; + } + + const InstancesHeader* pHeader = + reinterpret_cast(instancesBinary.data()); + + header = *pHeader; + headerLength = sizeof(InstancesHeader); + + if (pHeader->version != 1) { + result.errors.emplaceError(fmt::format( + "The I3DM file is version {}, which is unsupported.", + pHeader->version)); + return; + } + + if (static_cast(instancesBinary.size()) < pHeader->byteLength) { + result.errors.emplaceError( + "The I3DM is invalid because the total data available is less than the " + "size specified in its header."); + return; + } +} + +struct InstanceContent { + uint32_t instancesLength = 0; + glm::vec3 rtcCenter; + std::optional quantizedVolumeOffset; + std::optional quantizedVolumeScale; + bool eastNorthUp = false; + + // Offsets into the feature table. + std::optional position; + std::optional positionQuantized; + std::optional normalUp; + std::optional normalRight; + std::optional normalUpOct32p; + std::optional normalRightOct32p; + std::optional scale; + std::optional scaleNonUniform; + std::optional batchId; + // batch table format? + CesiumUtility::ErrorList errors; +}; + +glm::vec3 decodeOct32P(const uint16_t rawOct[2]) +{ + glm::dvec3 result = CesiumUtility::AttributeCompression::octDecodeInRange( + rawOct[0], rawOct[1], static_cast(65535)); + return glm::vec3(result); +} + +/* Calculate the rotation quaternion described by the up, right vectors passed in + * NORMAL_UP and NORMAL_RIGHT. This is composed of two rotations: + + The rotation that takes the up vector to its new position; + + The rotation around the new up vector that takes the right vector to its new position. + + I like to think of each rotation as describing a coordinate frame. The calculation of the second + rotation must take place within the first frame. + + The rotations are calculated by finding the rotation that takes one vector to another. If we take + the dot and cross products of the two vectors and store them in a quaternion, that quaternion + represents twice the required rotation. We get the correct quaternion by "averaging" with the zero + rotation quaternion, in a way analagous to finding the half vector between two 3D vectors. + */ + +glm::quat rotation(const glm::vec3& vec1, const glm::vec3& vec2) { + float cosRot = dot(vec1, vec2); + // Not using epsilon for these tests. If abs(cosRot) < 1.0, we can still + // create a sensible rotation. + if (cosRot >= 1.0f) { + // zero rotation + return glm::quat(1.0, 0.0f, 0.0f, 0.0f); + } + if (cosRot <= -1.0f) { + // Choose principal axis that is "most orthogonal" to the first vector. + glm::vec3 orthoVec(0.0f, 0.0f, 0.0f); + glm::vec3 absVals(std::fabs(vec1.x), std::fabs(vec1.y), std::fabs(vec1.z)); + if (absVals.z <= absVals.x && absVals.z <= absVals.y) { + orthoVec.z = 1.0f; + } else if (absVals.x <= absVals.y) { + orthoVec.x = 1.0f; + } else { + orthoVec.y = 1.0f; + } + auto rotAxis = cross(vec1, orthoVec); + rotAxis = normalize(rotAxis); + // rotation by pi radians + return glm::quat(0.0f, rotAxis.x, rotAxis.y, rotAxis.z); + } + auto rotAxis = cross(vec1, vec2); + glm::quat sumQuat(cosRot + 1.0f, rotAxis.x, rotAxis.y, rotAxis.z); + return normalize(sumQuat); +} + +glm::quat rotationFromUpRight(const glm::vec3& up, const glm::vec3& right) { + // First rotation: up + auto upRot = rotation(glm::vec3(0.0f, 1.0f, 0.0f), up); + // We can rotate a point vector by a quaternion using q * (0, v) * + // conj(q). But here we are doing an inverse rotation of the right vector into + // the "up frame." + glm::quat temp = conjugate(upRot) * glm::quat(0.0f, right.x, right.y, right.z) * upRot; + glm::vec3 innerRight(temp.x, temp.y, temp.z); + glm::quat rightRot = rotation(glm::vec3(1.0f, 0.0f, 0.0f), innerRight); + return upRot * rightRot; +} + +/* The approach: + + Parse the i3dm header, decoding and creating all the instance transforms. This includes + "exotic" things like OCT encoding of rotations and ENU rotations for each + instance. + + For each node with a mesh (a "mesh node"), the instance transforms must be + transformed into the local coordinates of the mesh and then stored in the + EXT_mesh_gpu_instancing extension for that node. It would be nice to avoid a + lot of duplicate data for mesh nodes with the same chain of transforms to + the tile root. One way to do this would be to store the accessors in a hash + table, hashed by mesh transform. + + Add the instance transforms to the glTF buffers, buffer views, and + accessors. + + Metadata / feature id? +*/ + +void convertInstancesContent( + const gsl::span& instancesBinary, + const InstancesHeader& header, + uint32_t headerLength, + const CesiumGltfReader::GltfReaderOptions& options, + ConverterSubprocessor* subprocessor, + GltfConverterResult& result, + DecodedInstances& decodedInstances) { + if (header.featureTableJsonByteLength == 0 || header.featureTableBinaryByteLength == 0) { + return; + } + 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> assetFuture; + if (header.gltfFormat == 0) { + // Need to recursively read the glTF content. + auto gltfUri = std::string(reinterpret_cast(gltfData.data()), gltfData.size()); + assetFuture = get(*subprocessor, gltfUri); + } + auto featureTableJsonData = + instancesBinary.subspan(headerLength, header.featureTableJsonByteLength); + rapidjson::Document featureTableJson; + featureTableJson.Parse( + reinterpret_cast(featureTableJsonData.data()), + featureTableJsonData.size()); + if (featureTableJson.HasParseError()) { + result.errors.emplaceError(fmt::format( + "Error when parsing feature table JSON, error code {} at byte offset " + "{}", + featureTableJson.GetParseError(), + featureTableJson.GetErrorOffset())); + return; + } + InstanceContent parsedContent; + // Global semantics + if (auto optinstancesLength = getValue(featureTableJson, "INSTANCES_LENGTH")) { + parsedContent.instancesLength = *optinstancesLength; + } else { + result.errors.emplaceError( + "Error parsing I3DM feature table, no valid INSTANCES_LENGTH was found."); + return; + } + if (auto optRtcCenter = parseArrayValueVec3(featureTableJson, "RTC_CENTER")) { + parsedContent.rtcCenter = *optRtcCenter; + } + parsedContent.position = parseOffset(featureTableJson, "POSITION", result.errors); + if (!parsedContent.position) { + if (result.errors.hasErrors()) { + return; + } + parsedContent.positionQuantized = parseOffset( featureTableJson, "POSITION_QUANTIZED", result.errors); + if (result.errors.hasErrors()) { + return; + } + } + if (parsedContent.positionQuantized) { + parsedContent.quantizedVolumeOffset = parseArrayValueVec3(featureTableJson, "QUANTIZED_VOLUME_OFFSET"); + if (!parsedContent.quantizedVolumeOffset) { + result.errors.emplaceError("Error parsing I3DM feature table, No valid QUANTIZED_VOLUME_OFFSET property"); + return; + } + parsedContent.quantizedVolumeScale = parseArrayValueVec3(featureTableJson, "QUANTIZED_VOLUME_SCALE"); + if (!parsedContent.quantizedVolumeScale) { + result.errors.emplaceError("Error parsing I3DM feature table, No valid QUANTIZED_VOLUME_SCALE property"); + return; + } + } + decodedInstances.rotationENU = false; + if (auto optENU = getValue(featureTableJson, "EAST_NORTH_UP")) { + parsedContent.eastNorthUp = *optENU; + decodedInstances.rotationENU = *optENU; + } + parsedContent.normalUp = parseOffset(featureTableJson, "NORMAL_UP", result.errors); + parsedContent.normalRight = parseOffset(featureTableJson, "NORMAL_RIGHT", result.errors); + parsedContent.normalUpOct32p = parseOffset(featureTableJson, "NORMAL_UP_OCT32P", result.errors); + parsedContent.normalRightOct32p = parseOffset(featureTableJson, "NORMAL_RIGHT_OCT32P", result.errors); + parsedContent.scale = parseOffset(featureTableJson, "SCALE", result.errors); + parsedContent.scaleNonUniform = parseOffset(featureTableJson, "SCALE_NON_UNIFORM", result.errors); + parsedContent.batchId = parseOffset(featureTableJson, "BATCH_ID", result.errors); + if (result.errors.hasErrors()) { + return; + } + auto featureTableBinaryData = + instancesBinary.subspan(headerLength + header.featureTableJsonByteLength, header.featureTableBinaryByteLength); + decodedInstances.positions.resize(parsedContent.instancesLength, parsedContent.rtcCenter); + if (parsedContent.position) { + const auto* rawPosition = reinterpret_cast(featureTableBinaryData.data() + *parsedContent.position); + for (unsigned i = 0; i < parsedContent.instancesLength; ++i) { + decodedInstances.positions[i] += rawPosition[i]; + } + } else { + const auto* rawQPosition = reinterpret_cast(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] = (*posQuantized)[j] / 65535.0f * (*parsedContent.quantizedVolumeScale)[j] + (*parsedContent.quantizedVolumeOffset)[j]; + } + decodedInstances.positions[i] += glm::vec3(position[0], position[1], position[2]); + } + } + decodedInstances.rotations.resize(parsedContent.instancesLength, glm::quat(1.0f, 0.0f, 0.0f, 0.0f)); + if (parsedContent.normalUp && parsedContent.normalRight) { + const auto* rawUp = reinterpret_cast(featureTableBinaryData.data() + *parsedContent.normalUp); + const auto* rawRight = reinterpret_cast(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(featureTableBinaryData.data() + *parsedContent.normalUpOct32p); + const auto* rawRightOct = reinterpret_cast(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); + } + } + decodedInstances.scales.resize(parsedContent.instancesLength, glm::vec3(1.0, 1.0, 1.0)); + if (parsedContent.scale) { + const auto* rawScale = reinterpret_cast(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(featureTableBinaryData.data() + *parsedContent.scaleNonUniform); + for (unsigned i = 0; i < parsedContent.instancesLength; ++i) { + decodedInstances.scales[i] = rawScaleNonUniform[i]; + } + } + ByteResult byteResult; + if (assetFuture) { + byteResult = assetFuture->wait(); + if (byteResult.errorList.hasErrors()) { + result.errors.merge(byteResult.errorList); + return; + } + } + GltfConverterResult binToGltfResult = + BinaryToGltfConverter::convert(assetFuture ? byteResult.bytes : gltfData, options, nullptr); + result.model = std::move(binToGltfResult.model); + result.errors.merge(std::move(binToGltfResult.errors)); +} + +// XXX If there are no scale or rotation parts to the instance transform, then there shouldn't be +// any after applying the forward and inverse glTF transforms. Should prove this! + +void instantiateInstances(GltfConverterResult& result, const DecodedInstances& decodedInstances) { + std::set meshNodes; + size_t totalStride = sizeof(float) * 3; + size_t rotOffset = 0; + const bool hasRotations = !decodedInstances.rotations.empty(); + if (hasRotations) { + rotOffset = sizeof(float) * 3; + totalStride += sizeof(float) * 4; + } + size_t scaleOffset = 0; + const bool hasScales = !decodedInstances.scales.empty(); + if (hasScales) { + scaleOffset = totalStride; + totalStride += sizeof(float) * 3; + } + int32_t instanceBufferId = createBufferInGltf(*result.model); + auto& instanceBuffer = result.model->buffers[static_cast(instanceBufferId)]; + int32_t instanceBufferViewId = createBufferViewInGltf(*result.model, instanceBufferId, 0, static_cast(totalStride)); + auto& instanceBufferView = result.model->bufferViews[static_cast(instanceBufferViewId)]; + const auto numInstances = static_cast(decodedInstances.positions.size()); + const size_t instanceDataSize = totalStride * numInstances; + auto upToZ = CesiumGltfContent::GltfUtilities::applyGltfUpAxisTransform(*result.model, glm::dmat4x4(1.0)); + result.model->forEachPrimitiveInScene( + -1, + [&](Model& gltf, Node& node, Mesh&, MeshPrimitive&, const glm::dmat4& transform) + { + auto [nodeItr, notSeen] = meshNodes.insert(&node); + if (!notSeen) { + return; + } + auto dataBaseOffset = static_cast(instanceBuffer.cesium.data.size()); + instanceBuffer.cesium.data.resize(dataBaseOffset + instanceDataSize); + // Transform instance transform into local glTF coordinate system. + const auto toTile = upToZ * transform; + const auto toTileInv = inverse(toTile); + for (unsigned i = 0; i < numInstances; ++i) { + auto instMat = toTileInv; + instMat = translate(instMat, glm::dvec3(decodedInstances.positions[i])); + if (hasRotations) { + instMat = instMat * toMat4(glm::dquat(decodedInstances.rotations[i])); + } + if (hasScales) { + instMat = scale(instMat, glm::dvec3(decodedInstances.scales[i])); + } + instMat = instMat * toTile; + glm::dvec3 position, scale, skew; + glm::dquat rotation; + glm::dvec4 perspective; + decompose(instMat, scale, rotation, position, skew, perspective); + glm::vec3 fposition(position); + std::memcpy(&instanceBuffer.cesium.data[i * totalStride], &fposition, sizeof(fposition)); + if (hasRotations) { + glm::quat frotation(rotation); + std::memcpy(&instanceBuffer.cesium.data[i * totalStride + rotOffset], &frotation, sizeof(frotation)); + } + if (hasScales) { + glm::vec3 fscale(scale); + std::memcpy(&instanceBuffer.cesium.data[i * totalStride + scaleOffset], &fscale, sizeof(fscale)); + } + } + auto& gpuExt = node.addExtension(); + if (!gpuExt.attributes.empty()) { + // wtf + } + auto posAccessorId = createAccessorInGltf(gltf, instanceBufferViewId, Accessor::ComponentType::FLOAT, numInstances, Accessor::Type::VEC3); + auto& posAcessor = gltf.accessors[static_cast(posAccessorId)]; + posAcessor.byteOffset = dataBaseOffset; + gpuExt.attributes["TRANSLATION"] = posAccessorId; + if (hasRotations) { + auto rotAccessorId = createAccessorInGltf(gltf, instanceBufferViewId, Accessor::ComponentType::FLOAT, numInstances, Accessor::Type::VEC4); + auto& rotAccessor = gltf.accessors[static_cast(rotAccessorId)]; + rotAccessor.byteOffset = static_cast(dataBaseOffset + rotOffset); + gpuExt.attributes["ROTATION"] = rotAccessorId; + } + if (hasScales) { + auto scaleAccessorId = createAccessorInGltf(gltf, instanceBufferViewId, Accessor::ComponentType::FLOAT, numInstances, Accessor::Type::VEC3); + auto& scaleAccessor = gltf.accessors[static_cast(scaleAccessorId)]; + scaleAccessor.byteOffset = static_cast(dataBaseOffset + scaleOffset); + gpuExt.attributes["SCALE"] = scaleAccessorId; + } + }); + instanceBuffer.byteLength = static_cast(instanceBuffer.cesium.data.size()); + instanceBufferView.byteLength = instanceBuffer.byteLength; +} +} + +GltfConverterResult I3dmToGltfConverter::convert( + const gsl::span& instancesBinary, + const CesiumGltfReader::GltfReaderOptions& options, + ConverterSubprocessor* subprocessor) { + GltfConverterResult result; + InstancesHeader header; + uint32_t headerLength = 0; + parseInstancesHeader(instancesBinary, header, headerLength, result); + if (result.errors) { + return result; + } + DecodedInstances decodedInstances; + convertInstancesContent(instancesBinary, header, headerLength, options, subprocessor, result, decodedInstances); + if (result.errors) { + return result; + } + instantiateInstances(result, decodedInstances); + return result; +} +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/LegacyUtilities.cpp b/Cesium3DTilesContent/src/LegacyUtilities.cpp new file mode 100644 index 000000000..b5a399773 --- /dev/null +++ b/Cesium3DTilesContent/src/LegacyUtilities.cpp @@ -0,0 +1,149 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +namespace Cesium3DTilesContent { +namespace LegacyUtilities { +using namespace CesiumGltf; + +std::optional parseOffset( const rapidjson::Document& document, const char* semantic, CesiumUtility::ErrorList& errorList) { + const auto semanticIt = document.FindMember(semantic); + if (semanticIt == document.MemberEnd() || !semanticIt->value.IsObject()) { + return {}; + } + const auto byteOffsetIt = semanticIt->value.FindMember("byteOffset"); + if (byteOffsetIt == semanticIt->value.MemberEnd() || !isValue(byteOffsetIt->value)) { + errorList.emplaceError( + std::string("Error parsing feature table, ") + semantic + "does not have valid byteOffset."); + return {}; + } + return getValue(byteOffsetIt->value); +} + +bool validateJsonArrayValues( + const rapidjson::Value& arrayValue, + uint32_t expectedLength, + ValuePredicate predicate) { + if (!arrayValue.IsArray()) { + return false; + } + + if (arrayValue.Size() != expectedLength) { + return false; + } + + for (rapidjson::SizeType i = 0; i < expectedLength; i++) { + if (!std::invoke(predicate, arrayValue[i])) { + return false; + } + } + + return true; +} + +std::optional parseArrayValueVec3(const rapidjson::Value& arrayValue) { + if (validateJsonArrayValues(arrayValue, 3, &rapidjson::Value::IsNumber)) { + return std::make_optional(glm::vec3(arrayValue[0].GetFloat(), + arrayValue[1].GetFloat(), + arrayValue[2].GetFloat())); + } + return {}; +} + +std::optional parseArrayValueVec3(const rapidjson::Document& document, const char *name) { + const auto arrayIt = document.FindMember(name); + if (arrayIt != document.MemberEnd()) { + return parseArrayValueVec3(arrayIt->value); + } + return {}; +} + +int32_t createBufferInGltf(Model& gltf) { + size_t bufferId = gltf.buffers.size(); + Buffer& gltfBuffer = gltf.buffers.emplace_back(); + gltfBuffer.byteLength = 0; + return static_cast(bufferId); +} + +int32_t createBufferInGltf(Model& gltf, std::vector&& buffer) { + int32_t bufferId = createBufferInGltf(gltf); + Buffer& gltfBuffer = gltf.buffers[static_cast(bufferId)]; + gltfBuffer.byteLength = static_cast(buffer.size()); + gltfBuffer.cesium.data = std::move(buffer); + return bufferId; +} + +int32_t createBufferViewInGltf( + Model& gltf, + const int32_t bufferId, + const int64_t byteLength, + const int64_t byteStride) { + size_t bufferViewId = gltf.bufferViews.size(); + BufferView& bufferView = gltf.bufferViews.emplace_back(); + bufferView.buffer = bufferId; + bufferView.byteLength = byteLength; + bufferView.byteOffset = 0; + bufferView.byteStride = byteStride; + bufferView.target = BufferView::Target::ARRAY_BUFFER; + + return static_cast(bufferViewId); +} + +int32_t createAccessorInGltf( + Model& gltf, + const int32_t bufferViewId, + const int32_t componentType, + const int64_t count, + const std::string type) { + size_t accessorId = gltf.accessors.size(); + Accessor& accessor = gltf.accessors.emplace_back(); + accessor.bufferView = bufferViewId; + accessor.byteOffset = 0; + accessor.componentType = componentType; + accessor.count = count; + accessor.type = type; + + return static_cast(accessorId); +} + +} + +CesiumAsync::Future 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&& 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 asset = pResponse->data(); + std::copy(asset.begin(), asset.end(), std::back_inserter(byteResult.bytes)); + return asyncSystem.createResolvedFuture(std::move(byteResult)); + }); +} +} diff --git a/Cesium3DTilesContent/src/PntsToGltfConverter.cpp b/Cesium3DTilesContent/src/PntsToGltfConverter.cpp index 3b2e50868..eaca77fd7 100644 --- a/Cesium3DTilesContent/src/PntsToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/PntsToGltfConverter.cpp @@ -1619,7 +1619,8 @@ void convertPntsContentToGltf( GltfConverterResult PntsToGltfConverter::convert( const gsl::span& pntsBinary, - const CesiumGltfReader::GltfReaderOptions& /*options*/) { + const CesiumGltfReader::GltfReaderOptions& /*options*/, + ConverterSubprocessor*) { GltfConverterResult result; PntsHeader header; uint32_t headerLength = 0; diff --git a/Cesium3DTilesContent/src/registerAllTileContentTypes.cpp b/Cesium3DTilesContent/src/registerAllTileContentTypes.cpp index 11d232f6b..844a31899 100644 --- a/Cesium3DTilesContent/src/registerAllTileContentTypes.cpp +++ b/Cesium3DTilesContent/src/registerAllTileContentTypes.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -11,6 +12,7 @@ void registerAllTileContentTypes() { GltfConverters::registerMagic("glTF", BinaryToGltfConverter::convert); GltfConverters::registerMagic("b3dm", B3dmToGltfConverter::convert); GltfConverters::registerMagic("cmpt", CmptToGltfConverter::convert); + GltfConverters::registerMagic("i3dm", I3dmToGltfConverter::convert); GltfConverters::registerMagic("pnts", PntsToGltfConverter::convert); GltfConverters::registerFileExtension( diff --git a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp index d6f9e1c94..d3cd63b0d 100644 --- a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp @@ -147,7 +147,8 @@ CesiumAsync::Future requestTileContent( CesiumGltfReader::GltfReaderOptions gltfOptions; gltfOptions.ktx2TranscodeTargets = ktx2TranscodeTargets; gltfOptions.applyTextureTransform = applyTextureTransform; - GltfConverterResult result = converter(responseData, gltfOptions); + GltfConverterResult result = + converter(responseData, gltfOptions, nullptr); // XXX // Report any errors if there are any logTileLoadResult(pLogger, tileUrl, result.errors); diff --git a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp index 0432aa53d..d340ece83 100644 --- a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp @@ -157,7 +157,8 @@ CesiumAsync::Future requestTileContent( CesiumGltfReader::GltfReaderOptions gltfOptions; gltfOptions.ktx2TranscodeTargets = ktx2TranscodeTargets; gltfOptions.applyTextureTransform = applyTextureTransform; - GltfConverterResult result = converter(responseData, gltfOptions); + GltfConverterResult result = + converter(responseData, gltfOptions, nullptr); // XXX // Report any errors if there are any logTileLoadResult(pLogger, tileUrl, result.errors); diff --git a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp index 70e0a028b..dc54f98ee 100644 --- a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp +++ b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp @@ -866,9 +866,11 @@ TilesetJsonLoader::loadTileContent(const TileLoadInput& loadInput) { tileTransform, tileRefine, upAxis = _upAxis, - externalContentInitializer = std::move(externalContentInitializer)]( - std::shared_ptr&& - pCompletedRequest) mutable { + externalContentInitializer = std::move(externalContentInitializer), + pAssetAccessor, + asyncSystem, + requestHeaders](std::shared_ptr&& + pCompletedRequest) mutable { auto pResponse = pCompletedRequest->response(); const std::string& tileUrl = pCompletedRequest->url(); if (!pResponse) { @@ -900,12 +902,19 @@ TilesetJsonLoader::loadTileContent(const TileLoadInput& loadInput) { if (converter) { // Convert to gltf + ConverterSubprocessor subprocessor{ + asyncSystem, + pAssetAccessor, + tileUrl, + tileTransform, + requestHeaders}; CesiumGltfReader::GltfReaderOptions gltfOptions; gltfOptions.ktx2TranscodeTargets = contentOptions.ktx2TranscodeTargets; gltfOptions.applyTextureTransform = contentOptions.applyTextureTransform; - GltfConverterResult result = converter(responseData, gltfOptions); + GltfConverterResult result = + converter(responseData, gltfOptions, &subprocessor); // Report any errors if there are any logTileLoadResult(pLogger, tileUrl, result.errors); From fe1f01d01c49a61d113f0da94d363e1fc34c0e83 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Tue, 9 Apr 2024 12:25:24 +0200 Subject: [PATCH 04/32] use npm format properly --- .../I3dmToGltfConverter.h | 3 +- .../Cesium3DTilesContent/LegacyUtilities.h | 64 ++-- .../src/I3dmToGltfConverter.cpp | 280 ++++++++++++------ Cesium3DTilesContent/src/LegacyUtilities.cpp | 90 +++--- 4 files changed, 276 insertions(+), 161 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/I3dmToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/I3dmToGltfConverter.h index 6526f6b15..1f11fc1f9 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/I3dmToGltfConverter.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/I3dmToGltfConverter.h @@ -1,7 +1,6 @@ #pragma once #include - #include #include @@ -17,6 +16,6 @@ struct I3dmToGltfConverter { static GltfConverterResult convert( const gsl::span& instancesBinary, const CesiumGltfReader::GltfReaderOptions& options, - ConverterSubprocessor* subprocessor); + ConverterSubprocessor* subprocessor); }; } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h index 64ccf360f..0dd014f62 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h @@ -1,13 +1,11 @@ #pragma once +#include #include #include - -#include #include #include - #include #include @@ -17,35 +15,39 @@ namespace CesiumGltf { class Model; class Buffer; -} +} // namespace CesiumGltf namespace Cesium3DTilesContent { struct ByteResult { - std::vector bytes; - CesiumUtility::ErrorList errorList; + std::vector bytes; + CesiumUtility::ErrorList errorList; }; -CesiumAsync::Future get(const ConverterSubprocessor& subprocessor, const std::string& relativeUrl); +CesiumAsync::Future +get(const ConverterSubprocessor& subprocessor, const std::string& relativeUrl); namespace LegacyUtilities { -std::optional parseOffset(const rapidjson::Document& document, const char* semantic, CesiumUtility::ErrorList& errorList); +std::optional parseOffset( + const rapidjson::Document& document, + const char* semantic, + CesiumUtility::ErrorList& errorList); typedef bool (rapidjson::Value::*ValuePredicate)() const; -template bool isValue(const rapidjson::Value& value); -template T getValue(const rapidjson::Value& value); +template bool isValue(const rapidjson::Value& value); +template T getValue(const rapidjson::Value& value); -template -std::optional getOpt(const rapidjson::Value& value) { +template std::optional getOpt(const rapidjson::Value& value) { if (isValue(value)) { return std::make_optional(getValue(value)); } return {}; } -template -std::optional getValue(const rapidjson::Document& document, const char *semantic) { +template +std::optional +getValue(const rapidjson::Document& document, const char* semantic) { const auto valueIt = document.FindMember(semantic); if (valueIt == document.MemberEnd() || !isValue(valueIt->value)) { return {}; @@ -53,34 +55,36 @@ std::optional getValue(const rapidjson::Document& document, const char *seman return std::make_optional(getValue(valueIt->value)); } -template<> -inline bool isValue(const rapidjson::Value& value) { +template <> inline bool isValue(const rapidjson::Value& value) { return value.IsBool(); } -template<> -inline bool getValue(const rapidjson::Value& value) { +template <> inline bool getValue(const rapidjson::Value& value) { return value.GetBool(); } -template<> -inline bool isValue(const rapidjson::Value& value) { - return value.IsUint(); +template <> inline bool isValue(const rapidjson::Value& value) { + return value.IsUint(); } -template<> -inline uint32_t getValue(const rapidjson::Value& value) { - return value.GetUint(); +template <> inline uint32_t getValue(const rapidjson::Value& value) { + return value.GetUint(); } -bool validateJsonArrayValues(const rapidjson::Value& arrayValue, uint32_t expectedLength, ValuePredicate predicate); +bool validateJsonArrayValues( + const rapidjson::Value& arrayValue, + uint32_t expectedLength, + ValuePredicate predicate); -std::optional parseArrayValueVec3(const rapidjson::Value& arrayValue); +std::optional +parseArrayValueVec3(const rapidjson::Value& arrayValue); -std::optional parseArrayValueVec3(const rapidjson::Document& document, const char *name); +std::optional +parseArrayValueVec3(const rapidjson::Document& document, const char* name); int32_t createBufferInGltf(CesiumGltf::Model& gltf); -int32_t createBufferInGltf(CesiumGltf::Model& gltf, std::vector&& buffer); +int32_t +createBufferInGltf(CesiumGltf::Model& gltf, std::vector&& buffer); int32_t createBufferViewInGltf( CesiumGltf::Model& gltf, @@ -94,5 +98,5 @@ int32_t createAccessorInGltf( const int32_t componentType, const int64_t count, const std::string type); -} -} +} // namespace LegacyUtilities +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp index 2075f6477..69dcebde7 100644 --- a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp @@ -41,7 +41,6 @@ struct DecodedInstances { bool rotationENU; }; - void parseInstancesHeader( const gsl::span& instancesBinary, InstancesHeader& header, @@ -95,25 +94,29 @@ struct InstanceContent { CesiumUtility::ErrorList errors; }; -glm::vec3 decodeOct32P(const uint16_t rawOct[2]) -{ - glm::dvec3 result = CesiumUtility::AttributeCompression::octDecodeInRange( - rawOct[0], rawOct[1], static_cast(65535)); - return glm::vec3(result); +glm::vec3 decodeOct32P(const uint16_t rawOct[2]) { + glm::dvec3 result = CesiumUtility::AttributeCompression::octDecodeInRange( + rawOct[0], + rawOct[1], + static_cast(65535)); + return glm::vec3(result); } -/* Calculate the rotation quaternion described by the up, right vectors passed in +/* Calculate the rotation quaternion described by the up, right vectors passed + in * NORMAL_UP and NORMAL_RIGHT. This is composed of two rotations: + The rotation that takes the up vector to its new position; - + The rotation around the new up vector that takes the right vector to its new position. + + The rotation around the new up vector that takes the right vector to its + new position. - I like to think of each rotation as describing a coordinate frame. The calculation of the second - rotation must take place within the first frame. + I like to think of each rotation as describing a coordinate frame. The + calculation of the second rotation must take place within the first frame. - The rotations are calculated by finding the rotation that takes one vector to another. If we take - the dot and cross products of the two vectors and store them in a quaternion, that quaternion - represents twice the required rotation. We get the correct quaternion by "averaging" with the zero - rotation quaternion, in a way analagous to finding the half vector between two 3D vectors. + The rotations are calculated by finding the rotation that takes one vector to + another. If we take the dot and cross products of the two vectors and store + them in a quaternion, that quaternion represents twice the required rotation. + We get the correct quaternion by "averaging" with the zero rotation quaternion, + in a way analagous to finding the half vector between two 3D vectors. */ glm::quat rotation(const glm::vec3& vec1, const glm::vec3& vec2) { @@ -137,7 +140,7 @@ glm::quat rotation(const glm::vec3& vec1, const glm::vec3& vec2) { } auto rotAxis = cross(vec1, orthoVec); rotAxis = normalize(rotAxis); - // rotation by pi radians + // rotation by pi radians return glm::quat(0.0f, rotAxis.x, rotAxis.y, rotAxis.z); } auto rotAxis = cross(vec1, vec2); @@ -150,17 +153,18 @@ glm::quat rotationFromUpRight(const glm::vec3& up, const glm::vec3& right) { auto upRot = rotation(glm::vec3(0.0f, 1.0f, 0.0f), up); // We can rotate a point vector by a quaternion using q * (0, v) * // conj(q). But here we are doing an inverse rotation of the right vector into - // the "up frame." - glm::quat temp = conjugate(upRot) * glm::quat(0.0f, right.x, right.y, right.z) * upRot; + // the "up frame." + glm::quat temp = + conjugate(upRot) * glm::quat(0.0f, right.x, right.y, right.z) * upRot; glm::vec3 innerRight(temp.x, temp.y, temp.z); glm::quat rightRot = rotation(glm::vec3(1.0f, 0.0f, 0.0f), innerRight); return upRot * rightRot; } /* The approach: - + Parse the i3dm header, decoding and creating all the instance transforms. This includes - "exotic" things like OCT encoding of rotations and ENU rotations for each - instance. + + Parse the i3dm header, decoding and creating all the instance transforms. + This includes "exotic" things like OCT encoding of rotations and ENU rotations + for each instance. + For each node with a mesh (a "mesh node"), the instance transforms must be transformed into the local coordinates of the mesh and then stored in the EXT_mesh_gpu_instancing extension for that node. It would be nice to avoid a @@ -180,19 +184,22 @@ void convertInstancesContent( ConverterSubprocessor* subprocessor, GltfConverterResult& result, DecodedInstances& decodedInstances) { - if (header.featureTableJsonByteLength == 0 || header.featureTableBinaryByteLength == 0) { + if (header.featureTableJsonByteLength == 0 || + header.featureTableBinaryByteLength == 0) { return; } const uint32_t glTFStart = headerLength + header.featureTableJsonByteLength + - header.featureTableBinaryByteLength + - header.batchTableJsonByteLength + - header.batchTableBinaryByteLength; + header.featureTableBinaryByteLength + + header.batchTableJsonByteLength + + header.batchTableBinaryByteLength; const uint32_t glTFEnd = header.byteLength; auto gltfData = instancesBinary.subspan(glTFStart, glTFEnd - glTFStart); std::optional> assetFuture; if (header.gltfFormat == 0) { // Need to recursively read the glTF content. - auto gltfUri = std::string(reinterpret_cast(gltfData.data()), gltfData.size()); + auto gltfUri = std::string( + reinterpret_cast(gltfData.data()), + gltfData.size()); assetFuture = get(*subprocessor, gltfUri); } auto featureTableJsonData = @@ -203,43 +210,50 @@ void convertInstancesContent( featureTableJsonData.size()); if (featureTableJson.HasParseError()) { result.errors.emplaceError(fmt::format( - "Error when parsing feature table JSON, error code {} at byte offset " - "{}", - featureTableJson.GetParseError(), - featureTableJson.GetErrorOffset())); + "Error when parsing feature table JSON, error code {} at byte offset " + "{}", + featureTableJson.GetParseError(), + featureTableJson.GetErrorOffset())); return; } InstanceContent parsedContent; // Global semantics - if (auto optinstancesLength = getValue(featureTableJson, "INSTANCES_LENGTH")) { + if (auto optinstancesLength = + getValue(featureTableJson, "INSTANCES_LENGTH")) { parsedContent.instancesLength = *optinstancesLength; } else { - result.errors.emplaceError( - "Error parsing I3DM feature table, no valid INSTANCES_LENGTH was found."); + result.errors.emplaceError("Error parsing I3DM feature table, no valid " + "INSTANCES_LENGTH was found."); return; } if (auto optRtcCenter = parseArrayValueVec3(featureTableJson, "RTC_CENTER")) { parsedContent.rtcCenter = *optRtcCenter; } - parsedContent.position = parseOffset(featureTableJson, "POSITION", result.errors); + parsedContent.position = + parseOffset(featureTableJson, "POSITION", result.errors); if (!parsedContent.position) { if (result.errors.hasErrors()) { return; } - parsedContent.positionQuantized = parseOffset( featureTableJson, "POSITION_QUANTIZED", result.errors); + parsedContent.positionQuantized = + parseOffset(featureTableJson, "POSITION_QUANTIZED", result.errors); if (result.errors.hasErrors()) { return; } } if (parsedContent.positionQuantized) { - parsedContent.quantizedVolumeOffset = parseArrayValueVec3(featureTableJson, "QUANTIZED_VOLUME_OFFSET"); + parsedContent.quantizedVolumeOffset = + parseArrayValueVec3(featureTableJson, "QUANTIZED_VOLUME_OFFSET"); if (!parsedContent.quantizedVolumeOffset) { - result.errors.emplaceError("Error parsing I3DM feature table, No valid QUANTIZED_VOLUME_OFFSET property"); + result.errors.emplaceError("Error parsing I3DM feature table, No valid " + "QUANTIZED_VOLUME_OFFSET property"); return; } - parsedContent.quantizedVolumeScale = parseArrayValueVec3(featureTableJson, "QUANTIZED_VOLUME_SCALE"); + parsedContent.quantizedVolumeScale = + parseArrayValueVec3(featureTableJson, "QUANTIZED_VOLUME_SCALE"); if (!parsedContent.quantizedVolumeScale) { - result.errors.emplaceError("Error parsing I3DM feature table, No valid QUANTIZED_VOLUME_SCALE property"); + result.errors.emplaceError("Error parsing I3DM feature table, No valid " + "QUANTIZED_VOLUME_SCALE property"); return; } } @@ -248,62 +262,88 @@ void convertInstancesContent( parsedContent.eastNorthUp = *optENU; decodedInstances.rotationENU = *optENU; } - parsedContent.normalUp = parseOffset(featureTableJson, "NORMAL_UP", result.errors); - parsedContent.normalRight = parseOffset(featureTableJson, "NORMAL_RIGHT", result.errors); - parsedContent.normalUpOct32p = parseOffset(featureTableJson, "NORMAL_UP_OCT32P", result.errors); - parsedContent.normalRightOct32p = parseOffset(featureTableJson, "NORMAL_RIGHT_OCT32P", result.errors); + parsedContent.normalUp = + parseOffset(featureTableJson, "NORMAL_UP", result.errors); + parsedContent.normalRight = + parseOffset(featureTableJson, "NORMAL_RIGHT", result.errors); + parsedContent.normalUpOct32p = + parseOffset(featureTableJson, "NORMAL_UP_OCT32P", result.errors); + parsedContent.normalRightOct32p = + parseOffset(featureTableJson, "NORMAL_RIGHT_OCT32P", result.errors); parsedContent.scale = parseOffset(featureTableJson, "SCALE", result.errors); - parsedContent.scaleNonUniform = parseOffset(featureTableJson, "SCALE_NON_UNIFORM", result.errors); - parsedContent.batchId = parseOffset(featureTableJson, "BATCH_ID", result.errors); + parsedContent.scaleNonUniform = + parseOffset(featureTableJson, "SCALE_NON_UNIFORM", result.errors); + parsedContent.batchId = + parseOffset(featureTableJson, "BATCH_ID", result.errors); if (result.errors.hasErrors()) { return; } - auto featureTableBinaryData = - instancesBinary.subspan(headerLength + header.featureTableJsonByteLength, header.featureTableBinaryByteLength); - decodedInstances.positions.resize(parsedContent.instancesLength, parsedContent.rtcCenter); + auto featureTableBinaryData = instancesBinary.subspan( + headerLength + header.featureTableJsonByteLength, + header.featureTableBinaryByteLength); + decodedInstances.positions.resize( + parsedContent.instancesLength, + parsedContent.rtcCenter); if (parsedContent.position) { - const auto* rawPosition = reinterpret_cast(featureTableBinaryData.data() + *parsedContent.position); + const auto* rawPosition = reinterpret_cast( + featureTableBinaryData.data() + *parsedContent.position); for (unsigned i = 0; i < parsedContent.instancesLength; ++i) { decodedInstances.positions[i] += rawPosition[i]; } } else { - const auto* rawQPosition = reinterpret_cast(featureTableBinaryData.data() + *parsedContent.positionQuantized); + const auto* rawQPosition = reinterpret_cast( + 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] = (*posQuantized)[j] / 65535.0f * (*parsedContent.quantizedVolumeScale)[j] + (*parsedContent.quantizedVolumeOffset)[j]; + position[j] = (*posQuantized)[j] / 65535.0f * + (*parsedContent.quantizedVolumeScale)[j] + + (*parsedContent.quantizedVolumeOffset)[j]; } - decodedInstances.positions[i] += glm::vec3(position[0], position[1], position[2]); + decodedInstances.positions[i] += + glm::vec3(position[0], position[1], position[2]); } } - decodedInstances.rotations.resize(parsedContent.instancesLength, glm::quat(1.0f, 0.0f, 0.0f, 0.0f)); + decodedInstances.rotations.resize( + parsedContent.instancesLength, + glm::quat(1.0f, 0.0f, 0.0f, 0.0f)); if (parsedContent.normalUp && parsedContent.normalRight) { - const auto* rawUp = reinterpret_cast(featureTableBinaryData.data() + *parsedContent.normalUp); - const auto* rawRight = reinterpret_cast(featureTableBinaryData.data() + *parsedContent.normalRight); + const auto* rawUp = reinterpret_cast( + featureTableBinaryData.data() + *parsedContent.normalUp); + const auto* rawRight = reinterpret_cast( + featureTableBinaryData.data() + *parsedContent.normalRight); for (unsigned i = 0; i < parsedContent.instancesLength; ++i) { - decodedInstances.rotations[i] = rotationFromUpRight(rawUp[i], rawRight[i]); + decodedInstances.rotations[i] = + rotationFromUpRight(rawUp[i], rawRight[i]); } } else if (parsedContent.normalUpOct32p && parsedContent.normalRightOct32p) { - const auto* rawUpOct = reinterpret_cast(featureTableBinaryData.data() + *parsedContent.normalUpOct32p); - const auto* rawRightOct = reinterpret_cast(featureTableBinaryData.data() + *parsedContent.normalRightOct32p); + const auto* rawUpOct = reinterpret_cast( + featureTableBinaryData.data() + *parsedContent.normalUpOct32p); + const auto* rawRightOct = reinterpret_cast( + featureTableBinaryData.data() + *parsedContent.normalRightOct32p); for (unsigned i = 0; i < parsedContent.instancesLength; ++i) { - glm::vec3 dUp = decodeOct32P(rawUpOct[i]); + glm::vec3 dUp = decodeOct32P(rawUpOct[i]); glm::vec3 dRight = decodeOct32P(rawRightOct[i]); decodedInstances.rotations[i] = rotationFromUpRight(dUp, dRight); } } - decodedInstances.scales.resize(parsedContent.instancesLength, glm::vec3(1.0, 1.0, 1.0)); + decodedInstances.scales.resize( + parsedContent.instancesLength, + glm::vec3(1.0, 1.0, 1.0)); if (parsedContent.scale) { - const auto* rawScale = reinterpret_cast(featureTableBinaryData.data() + *parsedContent.scale); + const auto* rawScale = reinterpret_cast( + featureTableBinaryData.data() + *parsedContent.scale); for (unsigned i = 0; i < parsedContent.instancesLength; ++i) { - decodedInstances.scales[i] = glm::vec3(rawScale[i], rawScale[i], rawScale[i]); + decodedInstances.scales[i] = + glm::vec3(rawScale[i], rawScale[i], rawScale[i]); } } else if (parsedContent.scaleNonUniform) { - const auto* rawScaleNonUniform = reinterpret_cast(featureTableBinaryData.data() + *parsedContent.scaleNonUniform); + const auto* rawScaleNonUniform = reinterpret_cast( + featureTableBinaryData.data() + *parsedContent.scaleNonUniform); for (unsigned i = 0; i < parsedContent.instancesLength; ++i) { decodedInstances.scales[i] = rawScaleNonUniform[i]; - } + } } ByteResult byteResult; if (assetFuture) { @@ -313,16 +353,21 @@ void convertInstancesContent( return; } } - GltfConverterResult binToGltfResult = - BinaryToGltfConverter::convert(assetFuture ? byteResult.bytes : gltfData, options, nullptr); + GltfConverterResult binToGltfResult = BinaryToGltfConverter::convert( + assetFuture ? byteResult.bytes : gltfData, + options, + nullptr); result.model = std::move(binToGltfResult.model); result.errors.merge(std::move(binToGltfResult.errors)); } -// XXX If there are no scale or rotation parts to the instance transform, then there shouldn't be -// any after applying the forward and inverse glTF transforms. Should prove this! +// XXX If there are no scale or rotation parts to the instance transform, then +// there shouldn't be any after applying the forward and inverse glTF +// transforms. Should prove this! -void instantiateInstances(GltfConverterResult& result, const DecodedInstances& decodedInstances) { +void instantiateInstances( + GltfConverterResult& result, + const DecodedInstances& decodedInstances) { std::set meshNodes; size_t totalStride = sizeof(float) * 3; size_t rotOffset = 0; @@ -338,30 +383,45 @@ void instantiateInstances(GltfConverterResult& result, const DecodedInstances& d totalStride += sizeof(float) * 3; } int32_t instanceBufferId = createBufferInGltf(*result.model); - auto& instanceBuffer = result.model->buffers[static_cast(instanceBufferId)]; - int32_t instanceBufferViewId = createBufferViewInGltf(*result.model, instanceBufferId, 0, static_cast(totalStride)); - auto& instanceBufferView = result.model->bufferViews[static_cast(instanceBufferViewId)]; - const auto numInstances = static_cast(decodedInstances.positions.size()); + auto& instanceBuffer = + result.model->buffers[static_cast(instanceBufferId)]; + int32_t instanceBufferViewId = createBufferViewInGltf( + *result.model, + instanceBufferId, + 0, + static_cast(totalStride)); + auto& instanceBufferView = + result.model->bufferViews[static_cast(instanceBufferViewId)]; + const auto numInstances = + static_cast(decodedInstances.positions.size()); const size_t instanceDataSize = totalStride * numInstances; - auto upToZ = CesiumGltfContent::GltfUtilities::applyGltfUpAxisTransform(*result.model, glm::dmat4x4(1.0)); + auto upToZ = CesiumGltfContent::GltfUtilities::applyGltfUpAxisTransform( + *result.model, + glm::dmat4x4(1.0)); result.model->forEachPrimitiveInScene( -1, - [&](Model& gltf, Node& node, Mesh&, MeshPrimitive&, const glm::dmat4& transform) - { + [&](Model& gltf, + Node& node, + Mesh&, + MeshPrimitive&, + const glm::dmat4& transform) { auto [nodeItr, notSeen] = meshNodes.insert(&node); if (!notSeen) { return; } - auto dataBaseOffset = static_cast(instanceBuffer.cesium.data.size()); + auto dataBaseOffset = + static_cast(instanceBuffer.cesium.data.size()); instanceBuffer.cesium.data.resize(dataBaseOffset + instanceDataSize); // Transform instance transform into local glTF coordinate system. const auto toTile = upToZ * transform; const auto toTileInv = inverse(toTile); for (unsigned i = 0; i < numInstances; ++i) { auto instMat = toTileInv; - instMat = translate(instMat, glm::dvec3(decodedInstances.positions[i])); + instMat = + translate(instMat, glm::dvec3(decodedInstances.positions[i])); if (hasRotations) { - instMat = instMat * toMat4(glm::dquat(decodedInstances.rotations[i])); + instMat = + instMat * toMat4(glm::dquat(decodedInstances.rotations[i])); } if (hasScales) { instMat = scale(instMat, glm::dvec3(decodedInstances.scales[i])); @@ -372,41 +432,70 @@ void instantiateInstances(GltfConverterResult& result, const DecodedInstances& d glm::dvec4 perspective; decompose(instMat, scale, rotation, position, skew, perspective); glm::vec3 fposition(position); - std::memcpy(&instanceBuffer.cesium.data[i * totalStride], &fposition, sizeof(fposition)); + std::memcpy( + &instanceBuffer.cesium.data[i * totalStride], + &fposition, + sizeof(fposition)); if (hasRotations) { glm::quat frotation(rotation); - std::memcpy(&instanceBuffer.cesium.data[i * totalStride + rotOffset], &frotation, sizeof(frotation)); + std::memcpy( + &instanceBuffer.cesium.data[i * totalStride + rotOffset], + &frotation, + sizeof(frotation)); } if (hasScales) { glm::vec3 fscale(scale); - std::memcpy(&instanceBuffer.cesium.data[i * totalStride + scaleOffset], &fscale, sizeof(fscale)); + std::memcpy( + &instanceBuffer.cesium.data[i * totalStride + scaleOffset], + &fscale, + sizeof(fscale)); } } auto& gpuExt = node.addExtension(); if (!gpuExt.attributes.empty()) { // wtf } - auto posAccessorId = createAccessorInGltf(gltf, instanceBufferViewId, Accessor::ComponentType::FLOAT, numInstances, Accessor::Type::VEC3); + auto posAccessorId = createAccessorInGltf( + gltf, + instanceBufferViewId, + Accessor::ComponentType::FLOAT, + numInstances, + Accessor::Type::VEC3); auto& posAcessor = gltf.accessors[static_cast(posAccessorId)]; posAcessor.byteOffset = dataBaseOffset; gpuExt.attributes["TRANSLATION"] = posAccessorId; if (hasRotations) { - auto rotAccessorId = createAccessorInGltf(gltf, instanceBufferViewId, Accessor::ComponentType::FLOAT, numInstances, Accessor::Type::VEC4); - auto& rotAccessor = gltf.accessors[static_cast(rotAccessorId)]; - rotAccessor.byteOffset = static_cast(dataBaseOffset + rotOffset); + auto rotAccessorId = createAccessorInGltf( + gltf, + instanceBufferViewId, + Accessor::ComponentType::FLOAT, + numInstances, + Accessor::Type::VEC4); + auto& rotAccessor = + gltf.accessors[static_cast(rotAccessorId)]; + rotAccessor.byteOffset = + static_cast(dataBaseOffset + rotOffset); gpuExt.attributes["ROTATION"] = rotAccessorId; } if (hasScales) { - auto scaleAccessorId = createAccessorInGltf(gltf, instanceBufferViewId, Accessor::ComponentType::FLOAT, numInstances, Accessor::Type::VEC3); - auto& scaleAccessor = gltf.accessors[static_cast(scaleAccessorId)]; - scaleAccessor.byteOffset = static_cast(dataBaseOffset + scaleOffset); + auto scaleAccessorId = createAccessorInGltf( + gltf, + instanceBufferViewId, + Accessor::ComponentType::FLOAT, + numInstances, + Accessor::Type::VEC3); + auto& scaleAccessor = + gltf.accessors[static_cast(scaleAccessorId)]; + scaleAccessor.byteOffset = + static_cast(dataBaseOffset + scaleOffset); gpuExt.attributes["SCALE"] = scaleAccessorId; } }); - instanceBuffer.byteLength = static_cast(instanceBuffer.cesium.data.size()); + instanceBuffer.byteLength = + static_cast(instanceBuffer.cesium.data.size()); instanceBufferView.byteLength = instanceBuffer.byteLength; } -} +} // namespace GltfConverterResult I3dmToGltfConverter::convert( const gsl::span& instancesBinary, @@ -420,9 +509,16 @@ GltfConverterResult I3dmToGltfConverter::convert( return result; } DecodedInstances decodedInstances; - convertInstancesContent(instancesBinary, header, headerLength, options, subprocessor, result, decodedInstances); + convertInstancesContent( + instancesBinary, + header, + headerLength, + options, + subprocessor, + result, + decodedInstances); if (result.errors) { - return result; + return result; } instantiateInstances(result, decodedInstances); return result; diff --git a/Cesium3DTilesContent/src/LegacyUtilities.cpp b/Cesium3DTilesContent/src/LegacyUtilities.cpp index b5a399773..6b99ec391 100644 --- a/Cesium3DTilesContent/src/LegacyUtilities.cpp +++ b/Cesium3DTilesContent/src/LegacyUtilities.cpp @@ -1,5 +1,4 @@ #include - #include #include #include @@ -18,15 +17,20 @@ namespace Cesium3DTilesContent { namespace LegacyUtilities { using namespace CesiumGltf; -std::optional parseOffset( const rapidjson::Document& document, const char* semantic, CesiumUtility::ErrorList& errorList) { +std::optional parseOffset( + const rapidjson::Document& document, + const char* semantic, + CesiumUtility::ErrorList& errorList) { const auto semanticIt = document.FindMember(semantic); if (semanticIt == document.MemberEnd() || !semanticIt->value.IsObject()) { return {}; } const auto byteOffsetIt = semanticIt->value.FindMember("byteOffset"); - if (byteOffsetIt == semanticIt->value.MemberEnd() || !isValue(byteOffsetIt->value)) { + if (byteOffsetIt == semanticIt->value.MemberEnd() || + !isValue(byteOffsetIt->value)) { errorList.emplaceError( - std::string("Error parsing feature table, ") + semantic + "does not have valid byteOffset."); + std::string("Error parsing feature table, ") + semantic + + "does not have valid byteOffset."); return {}; } return getValue(byteOffsetIt->value); @@ -53,16 +57,19 @@ bool validateJsonArrayValues( return true; } -std::optional parseArrayValueVec3(const rapidjson::Value& arrayValue) { +std::optional +parseArrayValueVec3(const rapidjson::Value& arrayValue) { if (validateJsonArrayValues(arrayValue, 3, &rapidjson::Value::IsNumber)) { - return std::make_optional(glm::vec3(arrayValue[0].GetFloat(), - arrayValue[1].GetFloat(), - arrayValue[2].GetFloat())); + return std::make_optional(glm::vec3( + arrayValue[0].GetFloat(), + arrayValue[1].GetFloat(), + arrayValue[2].GetFloat())); } return {}; } -std::optional parseArrayValueVec3(const rapidjson::Document& document, const char *name) { +std::optional +parseArrayValueVec3(const rapidjson::Document& document, const char* name) { const auto arrayIt = document.FindMember(name); if (arrayIt != document.MemberEnd()) { return parseArrayValueVec3(arrayIt->value); @@ -118,32 +125,41 @@ int32_t createAccessorInGltf( return static_cast(accessorId); } +} // namespace LegacyUtilities + +CesiumAsync::Future +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&& 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 asset = pResponse->data(); + std::copy( + asset.begin(), + asset.end(), + std::back_inserter(byteResult.bytes)); + return asyncSystem.createResolvedFuture(std::move(byteResult)); + }); } - -CesiumAsync::Future 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&& 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 asset = pResponse->data(); - std::copy(asset.begin(), asset.end(), std::back_inserter(byteResult.bytes)); - return asyncSystem.createResolvedFuture(std::move(byteResult)); - }); -} -} +} // namespace Cesium3DTilesContent From 72a8fb5201867b98cfd880c3fe7ccdb66f3e6765 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Wed, 10 Apr 2024 16:02:15 +0200 Subject: [PATCH 05/32] Fix convert calls in tests --- Cesium3DTilesContent/test/ConvertTileToGltf.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cesium3DTilesContent/test/ConvertTileToGltf.h b/Cesium3DTilesContent/test/ConvertTileToGltf.h index 2d343b59f..cbffcf9e3 100644 --- a/Cesium3DTilesContent/test/ConvertTileToGltf.h +++ b/Cesium3DTilesContent/test/ConvertTileToGltf.h @@ -11,11 +11,11 @@ namespace Cesium3DTilesContent { class ConvertTileToGltf { public: static GltfConverterResult fromB3dm(const std::filesystem::path& filePath) { - return B3dmToGltfConverter::convert(readFile(filePath), {}); + return B3dmToGltfConverter::convert(readFile(filePath), {}, nullptr); } static GltfConverterResult fromPnts(const std::filesystem::path& filePath) { - return PntsToGltfConverter::convert(readFile(filePath), {}); + return PntsToGltfConverter::convert(readFile(filePath), {}, nullptr); } }; } // namespace Cesium3DTilesContent From 9b2bde2626aa1186e8dab7f717cf0e92bcab56a2 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Thu, 11 Apr 2024 10:12:18 +0200 Subject: [PATCH 06/32] remove const from static_cast to satisfy gcc --- .../src/I3dmToGltfConverter.cpp | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp index 69dcebde7..886a7a016 100644 --- a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp @@ -98,25 +98,25 @@ glm::vec3 decodeOct32P(const uint16_t rawOct[2]) { glm::dvec3 result = CesiumUtility::AttributeCompression::octDecodeInRange( rawOct[0], rawOct[1], - static_cast(65535)); + static_cast(65535)); return glm::vec3(result); } -/* Calculate the rotation quaternion described by the up, right vectors passed - in - * NORMAL_UP and NORMAL_RIGHT. This is composed of two rotations: +/* + Calculate the rotation quaternion described by the up, right vectors passed + in NORMAL_UP and NORMAL_RIGHT. This is composed of two rotations: + The rotation that takes the up vector to its new position; + The rotation around the new up vector that takes the right vector to its - new position. + new position. I like to think of each rotation as describing a coordinate frame. The - calculation of the second rotation must take place within the first frame. + calculation of the second rotation must take place within the first frame. The rotations are calculated by finding the rotation that takes one vector to - another. If we take the dot and cross products of the two vectors and store - them in a quaternion, that quaternion represents twice the required rotation. - We get the correct quaternion by "averaging" with the zero rotation quaternion, - in a way analagous to finding the half vector between two 3D vectors. + another. If we take the dot and cross products of the two vectors and store + them in a quaternion, that quaternion represents twice the required rotation. + We get the correct quaternion by "averaging" with the zero rotation quaternion, + in a way analagous to finding the half vector between two 3D vectors. */ glm::quat rotation(const glm::vec3& vec1, const glm::vec3& vec2) { From b1c9f9484d2eeac5e07d3c01dcc039d4dbae925e Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Thu, 11 Apr 2024 11:02:17 +0200 Subject: [PATCH 07/32] More formatting fun --- Cesium3DTilesContent/src/I3dmToGltfConverter.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp index 886a7a016..b2f609813 100644 --- a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp @@ -115,8 +115,9 @@ glm::vec3 decodeOct32P(const uint16_t rawOct[2]) { The rotations are calculated by finding the rotation that takes one vector to another. If we take the dot and cross products of the two vectors and store them in a quaternion, that quaternion represents twice the required rotation. - We get the correct quaternion by "averaging" with the zero rotation quaternion, - in a way analagous to finding the half vector between two 3D vectors. + We get the correct quaternion by "averaging" with the zero rotation + quaternion, in a way analagous to finding the half vector between two 3D + vectors. */ glm::quat rotation(const glm::vec3& vec1, const glm::vec3& vec2) { From 37f3684ef98ff8e2145d44f6c9ae954186a09435 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Thu, 11 Apr 2024 11:54:30 +0200 Subject: [PATCH 08/32] Preserve double values from i3dm, WIP --- .../Cesium3DTilesContent/LegacyUtilities.h | 8 +++---- .../src/I3dmToGltfConverter.cpp | 22 ++++++++++--------- Cesium3DTilesContent/src/LegacyUtilities.cpp | 18 +++++++-------- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h index 0dd014f62..869d4ba4a 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h @@ -76,11 +76,11 @@ bool validateJsonArrayValues( uint32_t expectedLength, ValuePredicate predicate); -std::optional -parseArrayValueVec3(const rapidjson::Value& arrayValue); +std::optional +parseArrayValueDVec3(const rapidjson::Value& arrayValue); -std::optional -parseArrayValueVec3(const rapidjson::Document& document, const char* name); +std::optional +parseArrayValueDVec3(const rapidjson::Document& document, const char* name); int32_t createBufferInGltf(CesiumGltf::Model& gltf); int32_t diff --git a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp index b2f609813..16eeeeaeb 100644 --- a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp @@ -75,9 +75,9 @@ void parseInstancesHeader( struct InstanceContent { uint32_t instancesLength = 0; - glm::vec3 rtcCenter; - std::optional quantizedVolumeOffset; - std::optional quantizedVolumeScale; + glm::dvec3 rtcCenter; + std::optional quantizedVolumeOffset; + std::optional quantizedVolumeScale; bool eastNorthUp = false; // Offsets into the feature table. @@ -227,7 +227,8 @@ void convertInstancesContent( "INSTANCES_LENGTH was found."); return; } - if (auto optRtcCenter = parseArrayValueVec3(featureTableJson, "RTC_CENTER")) { + if (auto optRtcCenter = + parseArrayValueDVec3(featureTableJson, "RTC_CENTER")) { parsedContent.rtcCenter = *optRtcCenter; } parsedContent.position = @@ -244,14 +245,14 @@ void convertInstancesContent( } if (parsedContent.positionQuantized) { parsedContent.quantizedVolumeOffset = - parseArrayValueVec3(featureTableJson, "QUANTIZED_VOLUME_OFFSET"); + parseArrayValueDVec3(featureTableJson, "QUANTIZED_VOLUME_OFFSET"); if (!parsedContent.quantizedVolumeOffset) { result.errors.emplaceError("Error parsing I3DM feature table, No valid " "QUANTIZED_VOLUME_OFFSET property"); return; } parsedContent.quantizedVolumeScale = - parseArrayValueVec3(featureTableJson, "QUANTIZED_VOLUME_SCALE"); + parseArrayValueDVec3(featureTableJson, "QUANTIZED_VOLUME_SCALE"); if (!parsedContent.quantizedVolumeScale) { result.errors.emplaceError("Error parsing I3DM feature table, No valid " "QUANTIZED_VOLUME_SCALE property"); @@ -284,7 +285,7 @@ void convertInstancesContent( header.featureTableBinaryByteLength); decodedInstances.positions.resize( parsedContent.instancesLength, - parsedContent.rtcCenter); + glm::vec3(parsedContent.rtcCenter)); if (parsedContent.position) { const auto* rawPosition = reinterpret_cast( featureTableBinaryData.data() + *parsedContent.position); @@ -298,9 +299,10 @@ void convertInstancesContent( const auto* posQuantized = &rawQPosition[i]; float position[3]; for (unsigned j = 0; j < 3; ++j) { - position[j] = (*posQuantized)[j] / 65535.0f * - (*parsedContent.quantizedVolumeScale)[j] + - (*parsedContent.quantizedVolumeOffset)[j]; + position[j] = static_cast( + (*posQuantized)[j] / 65535.0 * + (*parsedContent.quantizedVolumeScale)[j] + + (*parsedContent.quantizedVolumeOffset)[j]); } decodedInstances.positions[i] += glm::vec3(position[0], position[1], position[2]); diff --git a/Cesium3DTilesContent/src/LegacyUtilities.cpp b/Cesium3DTilesContent/src/LegacyUtilities.cpp index 6b99ec391..8d2d00ce5 100644 --- a/Cesium3DTilesContent/src/LegacyUtilities.cpp +++ b/Cesium3DTilesContent/src/LegacyUtilities.cpp @@ -57,22 +57,22 @@ bool validateJsonArrayValues( return true; } -std::optional -parseArrayValueVec3(const rapidjson::Value& arrayValue) { +std::optional +parseArrayValueDVec3(const rapidjson::Value& arrayValue) { if (validateJsonArrayValues(arrayValue, 3, &rapidjson::Value::IsNumber)) { - return std::make_optional(glm::vec3( - arrayValue[0].GetFloat(), - arrayValue[1].GetFloat(), - arrayValue[2].GetFloat())); + return std::make_optional(glm::dvec3( + arrayValue[0].GetDouble(), + arrayValue[1].GetDouble(), + arrayValue[2].GetDouble())); } return {}; } -std::optional -parseArrayValueVec3(const rapidjson::Document& document, const char* name) { +std::optional +parseArrayDValueVec3(const rapidjson::Document& document, const char* name) { const auto arrayIt = document.FindMember(name); if (arrayIt != document.MemberEnd()) { - return parseArrayValueVec3(arrayIt->value); + return parseArrayValueDVec3(arrayIt->value); } return {}; } From 806d0edfec40d4eaef0249d87b6789e6855d8830 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Wed, 17 Apr 2024 12:40:24 +0200 Subject: [PATCH 09/32] WIP glTF converter functions return a Future --- .../B3dmToGltfConverter.h | 5 +- .../BinaryToGltfConverter.h | 7 +- .../CmptToGltfConverter.h | 3 +- .../Cesium3DTilesContent/GltfConverters.h | 8 +- .../I3dmToGltfConverter.h | 3 +- .../PntsToGltfConverter.h | 5 +- .../src/B3dmToGltfConverter.cpp | 49 ++--- .../src/BinaryToGltfConverter.cpp | 11 +- .../src/CmptToGltfConverter.cpp | 50 ++--- Cesium3DTilesContent/src/GltfConverters.cpp | 10 +- .../src/I3dmToGltfConverter.cpp | 188 +++++++++++------- Cesium3DTilesContent/src/LegacyUtilities.cpp | 2 +- .../src/PntsToGltfConverter.cpp | 12 +- .../src/ImplicitOctreeLoader.cpp | 69 ++++--- .../src/ImplicitQuadtreeLoader.cpp | 70 ++++--- .../src/TilesetJsonLoader.cpp | 157 +++++++-------- 16 files changed, 375 insertions(+), 274 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/B3dmToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/B3dmToGltfConverter.h index 9cd99b028..f45e4428e 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/B3dmToGltfConverter.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/B3dmToGltfConverter.h @@ -2,6 +2,7 @@ #include "GltfConverterResult.h" +#include #include #include @@ -13,9 +14,9 @@ namespace Cesium3DTilesContent { struct ConverterSubprocessor; struct B3dmToGltfConverter { - static GltfConverterResult convert( + static CesiumAsync::Future convert( const gsl::span& b3dmBinary, const CesiumGltfReader::GltfReaderOptions& options, - ConverterSubprocessor*); + ConverterSubprocessor* subprocessor); }; } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h index 6a2b499d1..c4c5da6e8 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -12,12 +13,16 @@ struct ConverterSubprocessor; struct BinaryToGltfConverter { public: - static GltfConverterResult convert( + static CesiumAsync::Future convert( const gsl::span& gltfBinary, const CesiumGltfReader::GltfReaderOptions& options, ConverterSubprocessor* subProcessor); private: + static GltfConverterResult convertImmediate( + const gsl::span& gltfBinary, + const CesiumGltfReader::GltfReaderOptions& options, + ConverterSubprocessor* subProcessor); static CesiumGltfReader::GltfReader _gltfReader; }; } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/CmptToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/CmptToGltfConverter.h index 50f2cccae..2d02f3ef3 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/CmptToGltfConverter.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/CmptToGltfConverter.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -11,7 +12,7 @@ namespace Cesium3DTilesContent { struct ConverterSubprocessor; struct CmptToGltfConverter { - static GltfConverterResult convert( + static CesiumAsync::Future convert( const gsl::span& cmptBinary, const CesiumGltfReader::GltfReaderOptions& options, ConverterSubprocessor*); diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h index 3a1f71346..e45d9dc91 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h @@ -3,6 +3,8 @@ #include "Library.h" #include +#include +#include #include #include @@ -56,7 +58,7 @@ class CESIUM3DTILESCONTENT_API GltfConverters { * @brief A function pointer that can create a {@link GltfConverterResult} from a * tile binary content. */ - using ConverterFunction = GltfConverterResult (*)( + using ConverterFunction = CesiumAsync::Future (*)( const gsl::span& content, const CesiumGltfReader::GltfReaderOptions& options, ConverterSubprocessor* subprocessor); @@ -145,7 +147,7 @@ class CESIUM3DTILESCONTENT_API GltfConverters { * @param options The {@link CesiumGltfReader::GltfReaderOptions} for how to read a glTF. * @return The {@link GltfConverterResult} that stores the gltf model converted from the binary data. */ - static GltfConverterResult convert( + static CesiumAsync::Future convert( const std::string& filePath, const gsl::span& content, const CesiumGltfReader::GltfReaderOptions& options, @@ -171,7 +173,7 @@ class CESIUM3DTILESCONTENT_API GltfConverters { * @param options The {@link CesiumGltfReader::GltfReaderOptions} for how to read a glTF. * @return The {@link GltfConverterResult} that stores the gltf model converted from the binary data. */ - static GltfConverterResult convert( + static CesiumAsync::Future convert( const gsl::span& content, const CesiumGltfReader::GltfReaderOptions& options, ConverterSubprocessor* subprocessor); diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/I3dmToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/I3dmToGltfConverter.h index 1f11fc1f9..0d15ad314 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/I3dmToGltfConverter.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/I3dmToGltfConverter.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -13,7 +14,7 @@ namespace Cesium3DTilesContent { struct ConverterSubprocessor; struct I3dmToGltfConverter { - static GltfConverterResult convert( + static CesiumAsync::Future convert( const gsl::span& instancesBinary, const CesiumGltfReader::GltfReaderOptions& options, ConverterSubprocessor* subprocessor); diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/PntsToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/PntsToGltfConverter.h index 963071cd9..61fd763f7 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/PntsToGltfConverter.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/PntsToGltfConverter.h @@ -2,6 +2,7 @@ #include "GltfConverterResult.h" +#include #include #include @@ -13,9 +14,9 @@ namespace Cesium3DTilesContent { struct ConverterSubprocessor; struct PntsToGltfConverter { - static GltfConverterResult convert( + static CesiumAsync::Future convert( const gsl::span& pntsBinary, const CesiumGltfReader::GltfReaderOptions& options, - ConverterSubprocessor*); + ConverterSubprocessor* subprocessor); }; } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/B3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/B3dmToGltfConverter.cpp index 99e2620ed..140514c93 100644 --- a/Cesium3DTilesContent/src/B3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/B3dmToGltfConverter.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -113,12 +114,12 @@ void parseB3dmHeader( } } -void convertB3dmContentToGltf( +CesiumAsync::Future convertB3dmContentToGltf( const gsl::span& b3dmBinary, const B3dmHeader& header, uint32_t headerLength, const CesiumGltfReader::GltfReaderOptions& options, - GltfConverterResult& result) { + ConverterSubprocessor* subprocessor) { const uint32_t glbStart = headerLength + header.featureTableJsonByteLength + header.featureTableBinaryByteLength + header.batchTableJsonByteLength + @@ -126,20 +127,17 @@ void convertB3dmContentToGltf( const uint32_t glbEnd = header.byteLength; if (glbEnd <= glbStart) { + GltfConverterResult result; result.errors.emplaceError( "The B3DM is invalid because the start of the " "glTF model is after the end of the entire B3DM."); - return; + return subprocessor->asyncSystem.createResolvedFuture(std::move(result)); } const gsl::span glbData = b3dmBinary.subspan(glbStart, glbEnd - glbStart); - GltfConverterResult binToGltfResult = - BinaryToGltfConverter::convert(glbData, options, nullptr); - - result.model = std::move(binToGltfResult.model); - result.errors.merge(std::move(binToGltfResult.errors)); + return BinaryToGltfConverter::convert(glbData, options, subprocessor); } rapidjson::Document parseFeatureTableJsonData( @@ -229,29 +227,34 @@ void convertB3dmMetadataToGltfStructuralMetadata( } } // namespace -GltfConverterResult B3dmToGltfConverter::convert( +CesiumAsync::Future B3dmToGltfConverter::convert( const gsl::span& b3dmBinary, const CesiumGltfReader::GltfReaderOptions& options, - ConverterSubprocessor*) { + ConverterSubprocessor* subprocessor) { GltfConverterResult result; B3dmHeader header; uint32_t headerLength = 0; parseB3dmHeader(b3dmBinary, header, headerLength, result); if (result.errors) { - return result; + return subprocessor->asyncSystem.createResolvedFuture(std::move(result)); } - convertB3dmContentToGltf(b3dmBinary, header, headerLength, options, result); - if (result.errors) { - return result; - } - - convertB3dmMetadataToGltfStructuralMetadata( - b3dmBinary, - header, - headerLength, - result); - - return result; + return convertB3dmContentToGltf( + b3dmBinary, + header, + headerLength, + options, + subprocessor) + .thenImmediately( + [b3dmBinary, header, headerLength](GltfConverterResult&& glbResult) { + if (!glbResult.errors) { + convertB3dmMetadataToGltfStructuralMetadata( + b3dmBinary, + header, + headerLength, + glbResult); + } + return glbResult; + }); } } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp b/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp index 8c96973f3..c3dfd83d2 100644 --- a/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp @@ -1,9 +1,10 @@ #include +#include namespace Cesium3DTilesContent { CesiumGltfReader::GltfReader BinaryToGltfConverter::_gltfReader; -GltfConverterResult BinaryToGltfConverter::convert( +GltfConverterResult BinaryToGltfConverter::convertImmediate( const gsl::span& gltfBinary, const CesiumGltfReader::GltfReaderOptions& options, ConverterSubprocessor*) { @@ -16,4 +17,12 @@ GltfConverterResult BinaryToGltfConverter::convert( result.errors.warnings = std::move(loadedGltf.warnings); return result; } + +CesiumAsync::Future BinaryToGltfConverter::convert( + const gsl::span& gltfBinary, + const CesiumGltfReader::GltfReaderOptions& options, + ConverterSubprocessor* subprocessor) { + return subprocessor->asyncSystem.createResolvedFuture( + convertImmediate(gltfBinary, options, subprocessor)); +} } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/CmptToGltfConverter.cpp b/Cesium3DTilesContent/src/CmptToGltfConverter.cpp index ba287397e..bd35c5d1a 100644 --- a/Cesium3DTilesContent/src/CmptToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/CmptToGltfConverter.cpp @@ -22,14 +22,14 @@ static_assert(sizeof(CmptHeader) == 16); static_assert(sizeof(InnerHeader) == 12); } // namespace -GltfConverterResult CmptToGltfConverter::convert( +CesiumAsync::Future CmptToGltfConverter::convert( const gsl::span& cmptBinary, const CesiumGltfReader::GltfReaderOptions& options, ConverterSubprocessor* subProcessor) { GltfConverterResult result; if (cmptBinary.size() < sizeof(CmptHeader)) { result.errors.emplaceWarning("Composite tile must be at least 16 bytes."); - return result; + return subProcessor->asyncSystem.createResolvedFuture(std::move(result)); } const CmptHeader* pHeader = @@ -37,14 +37,14 @@ GltfConverterResult CmptToGltfConverter::convert( if (std::string(pHeader->magic, 4) != "cmpt") { result.errors.emplaceWarning( "Composite tile does not have the expected magic vaue 'cmpt'."); - return result; + return subProcessor->asyncSystem.createResolvedFuture(std::move(result)); } if (pHeader->version != 1) { result.errors.emplaceWarning(fmt::format( "Unsupported composite tile version {}.", pHeader->version)); - return result; + return subProcessor->asyncSystem.createResolvedFuture(std::move(result)); } if (pHeader->byteLength > cmptBinary.size()) { @@ -52,10 +52,10 @@ GltfConverterResult CmptToGltfConverter::convert( "Composite tile byteLength is {} but only {} bytes are available.", pHeader->byteLength, cmptBinary.size())); - return result; + return subProcessor->asyncSystem.createResolvedFuture(std::move(result)); } - std::vector innerTiles; + std::vector> innerTiles; uint32_t pos = sizeof(CmptHeader); for (uint32_t i = 0; i < pHeader->tilesLength && pos < pHeader->byteLength; @@ -90,26 +90,26 @@ GltfConverterResult CmptToGltfConverter::convert( "Composite tile does not contain any loadable inner " "tiles."); } - - return result; - } - - if (innerTiles.size() == 1) { - return std::move(innerTiles[0]); - } - - for (size_t i = 0; i < innerTiles.size(); ++i) { - if (innerTiles[i].model) { - if (result.model) { - result.model->merge(std::move(*innerTiles[i].model)); - } else { - result.model = std::move(innerTiles[i].model); - } - } - - result.errors.merge(innerTiles[i].errors); + return subProcessor->asyncSystem.createResolvedFuture(std::move(result)); } - return result; + return subProcessor->asyncSystem.all(std::move(innerTiles)) + .thenImmediately([](std::vector&& innerResults) { + if (innerResults.size() == 1) { + return innerResults[0]; + } + GltfConverterResult cmptResult; + for (auto& innerTile : innerResults) { + if (innerTile.model) { + if (cmptResult.model) { + cmptResult.model->merge(std::move(*innerTile.model)); + } else { + cmptResult.model = std::move(innerTile.model); + } + } + cmptResult.errors.merge(innerTile.errors); + } + return cmptResult; + }); } } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/GltfConverters.cpp b/Cesium3DTilesContent/src/GltfConverters.cpp index 0617c33c2..02423b836 100644 --- a/Cesium3DTilesContent/src/GltfConverters.cpp +++ b/Cesium3DTilesContent/src/GltfConverters.cpp @@ -37,7 +37,7 @@ GltfConverters::getConverterByMagic(const gsl::span& content) { return getConverterByMagic(content, magic); } -GltfConverterResult GltfConverters::convert( +CesiumAsync::Future GltfConverters::convert( const std::string& filePath, const gsl::span& content, const CesiumGltfReader::GltfReaderOptions& options, @@ -61,10 +61,11 @@ GltfConverterResult GltfConverters::convert( fileExtension, magic)); - return GltfConverterResult{std::nullopt, std::move(errors)}; + return subprocessor->asyncSystem.createResolvedFuture( + GltfConverterResult{std::nullopt, std::move(errors)}); } -GltfConverterResult GltfConverters::convert( +CesiumAsync::Future GltfConverters::convert( const gsl::span& content, const CesiumGltfReader::GltfReaderOptions& options, ConverterSubprocessor* subprocessor) { @@ -79,7 +80,8 @@ GltfConverterResult GltfConverters::convert( "No loader registered for tile with magic value '{}'", magic)); - return GltfConverterResult{std::nullopt, std::move(errors)}; + return subprocessor->asyncSystem.createResolvedFuture( + GltfConverterResult{std::nullopt, std::move(errors)}); } std::string GltfConverters::toLowerCase(const std::string_view& str) { diff --git a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp index 16eeeeaeb..4aa41acf4 100644 --- a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp @@ -162,6 +162,11 @@ glm::quat rotationFromUpRight(const glm::vec3& up, const glm::vec3& right) { return upRot * rightRot; } +struct ConvertResult { + GltfConverterResult gltfResult; + DecodedInstances decodedInstances; +}; + /* The approach: + Parse the i3dm header, decoding and creating all the instance transforms. This includes "exotic" things like OCT encoding of rotations and ENU rotations @@ -177,17 +182,19 @@ glm::quat rotationFromUpRight(const glm::vec3& up, const glm::vec3& right) { + Metadata / feature id? */ -void convertInstancesContent( +CesiumAsync::Future convertInstancesContent( const gsl::span& instancesBinary, const InstancesHeader& header, uint32_t headerLength, const CesiumGltfReader::GltfReaderOptions& options, ConverterSubprocessor* subprocessor, - GltfConverterResult& result, - DecodedInstances& decodedInstances) { + GltfConverterResult& result) { + ConvertResult subResult; + DecodedInstances& decodedInstances = subResult.decodedInstances; + subResult.gltfResult = result; if (header.featureTableJsonByteLength == 0 || header.featureTableBinaryByteLength == 0) { - return; + return subprocessor->asyncSystem.createResolvedFuture(std::move(subResult)); } const uint32_t glTFStart = headerLength + header.featureTableJsonByteLength + header.featureTableBinaryByteLength + @@ -196,13 +203,6 @@ void convertInstancesContent( const uint32_t glTFEnd = header.byteLength; auto gltfData = instancesBinary.subspan(glTFStart, glTFEnd - glTFStart); std::optional> assetFuture; - if (header.gltfFormat == 0) { - // Need to recursively read the glTF content. - auto gltfUri = std::string( - reinterpret_cast(gltfData.data()), - gltfData.size()); - assetFuture = get(*subprocessor, gltfUri); - } auto featureTableJsonData = instancesBinary.subspan(headerLength, header.featureTableJsonByteLength); rapidjson::Document featureTableJson; @@ -210,12 +210,12 @@ void convertInstancesContent( reinterpret_cast(featureTableJsonData.data()), featureTableJsonData.size()); if (featureTableJson.HasParseError()) { - result.errors.emplaceError(fmt::format( + subResult.gltfResult.errors.emplaceError(fmt::format( "Error when parsing feature table JSON, error code {} at byte offset " "{}", featureTableJson.GetParseError(), featureTableJson.GetErrorOffset())); - return; + return subprocessor->asyncSystem.createResolvedFuture(std::move(subResult)); } InstanceContent parsedContent; // Global semantics @@ -223,40 +223,49 @@ void convertInstancesContent( getValue(featureTableJson, "INSTANCES_LENGTH")) { parsedContent.instancesLength = *optinstancesLength; } else { - result.errors.emplaceError("Error parsing I3DM feature table, no valid " - "INSTANCES_LENGTH was found."); - return; + subResult.gltfResult.errors.emplaceError( + "Error parsing I3DM feature table, no valid " + "INSTANCES_LENGTH was found."); + return subprocessor->asyncSystem.createResolvedFuture(std::move(subResult)); } if (auto optRtcCenter = parseArrayValueDVec3(featureTableJson, "RTC_CENTER")) { parsedContent.rtcCenter = *optRtcCenter; } parsedContent.position = - parseOffset(featureTableJson, "POSITION", result.errors); + parseOffset(featureTableJson, "POSITION", subResult.gltfResult.errors); if (!parsedContent.position) { - if (result.errors.hasErrors()) { - return; + if (subResult.gltfResult.errors.hasErrors()) { + return subprocessor->asyncSystem.createResolvedFuture( + std::move(subResult)); } - parsedContent.positionQuantized = - parseOffset(featureTableJson, "POSITION_QUANTIZED", result.errors); - if (result.errors.hasErrors()) { - return; + parsedContent.positionQuantized = parseOffset( + featureTableJson, + "POSITION_QUANTIZED", + subResult.gltfResult.errors); + if (subResult.gltfResult.errors.hasErrors()) { + return subprocessor->asyncSystem.createResolvedFuture( + std::move(subResult)); } } if (parsedContent.positionQuantized) { parsedContent.quantizedVolumeOffset = parseArrayValueDVec3(featureTableJson, "QUANTIZED_VOLUME_OFFSET"); if (!parsedContent.quantizedVolumeOffset) { - result.errors.emplaceError("Error parsing I3DM feature table, No valid " - "QUANTIZED_VOLUME_OFFSET property"); - return; + subResult.gltfResult.errors.emplaceError( + "Error parsing I3DM feature table, No valid " + "QUANTIZED_VOLUME_OFFSET property"); + return subprocessor->asyncSystem.createResolvedFuture( + std::move(subResult)); } parsedContent.quantizedVolumeScale = parseArrayValueDVec3(featureTableJson, "QUANTIZED_VOLUME_SCALE"); if (!parsedContent.quantizedVolumeScale) { - result.errors.emplaceError("Error parsing I3DM feature table, No valid " - "QUANTIZED_VOLUME_SCALE property"); - return; + subResult.gltfResult.errors.emplaceError( + "Error parsing I3DM feature table, No valid " + "QUANTIZED_VOLUME_SCALE property"); + return subprocessor->asyncSystem.createResolvedFuture( + std::move(subResult)); } } decodedInstances.rotationENU = false; @@ -265,20 +274,29 @@ void convertInstancesContent( decodedInstances.rotationENU = *optENU; } parsedContent.normalUp = - parseOffset(featureTableJson, "NORMAL_UP", result.errors); - parsedContent.normalRight = - parseOffset(featureTableJson, "NORMAL_RIGHT", result.errors); - parsedContent.normalUpOct32p = - parseOffset(featureTableJson, "NORMAL_UP_OCT32P", result.errors); - parsedContent.normalRightOct32p = - parseOffset(featureTableJson, "NORMAL_RIGHT_OCT32P", result.errors); - parsedContent.scale = parseOffset(featureTableJson, "SCALE", result.errors); - parsedContent.scaleNonUniform = - parseOffset(featureTableJson, "SCALE_NON_UNIFORM", result.errors); + parseOffset(featureTableJson, "NORMAL_UP", subResult.gltfResult.errors); + parsedContent.normalRight = parseOffset( + featureTableJson, + "NORMAL_RIGHT", + subResult.gltfResult.errors); + parsedContent.normalUpOct32p = parseOffset( + featureTableJson, + "NORMAL_UP_OCT32P", + subResult.gltfResult.errors); + parsedContent.normalRightOct32p = parseOffset( + featureTableJson, + "NORMAL_RIGHT_OCT32P", + subResult.gltfResult.errors); + parsedContent.scale = + parseOffset(featureTableJson, "SCALE", subResult.gltfResult.errors); + parsedContent.scaleNonUniform = parseOffset( + featureTableJson, + "SCALE_NON_UNIFORM", + subResult.gltfResult.errors); parsedContent.batchId = - parseOffset(featureTableJson, "BATCH_ID", result.errors); - if (result.errors.hasErrors()) { - return; + parseOffset(featureTableJson, "BATCH_ID", subResult.gltfResult.errors); + if (subResult.gltfResult.errors.hasErrors()) { + return subprocessor->asyncSystem.createResolvedFuture(std::move(subResult)); } auto featureTableBinaryData = instancesBinary.subspan( headerLength + header.featureTableJsonByteLength, @@ -349,19 +367,46 @@ void convertInstancesContent( } } ByteResult byteResult; - if (assetFuture) { - byteResult = assetFuture->wait(); - if (byteResult.errorList.hasErrors()) { - result.errors.merge(byteResult.errorList); - return; - } + if (header.gltfFormat == 0) { + // Need to recursively read the glTF content. + auto gltfUri = std::string( + reinterpret_cast(gltfData.data()), + gltfData.size()); + return get(*subprocessor, gltfUri) + .thenImmediately( + [options, subprocessor](ByteResult&& byteResult) + -> CesiumAsync::Future { + if (byteResult.errorList.hasErrors()) { + GltfConverterResult errorResult; + errorResult.errors.merge(byteResult.errorList); + return subprocessor->asyncSystem.createResolvedFuture( + std::move(errorResult)); + } + return BinaryToGltfConverter::convert( + byteResult.bytes, + options, + subprocessor); + }) + .thenImmediately([subResult = std::move(subResult)]( + GltfConverterResult&& converterResult) mutable { + if (converterResult.errors.hasErrors()) { + subResult.gltfResult.errors.merge(converterResult.errors); + } else { + subResult.gltfResult = converterResult; + } + return subResult; + }); + } else { + return BinaryToGltfConverter::convert(gltfData, options, subprocessor) + .thenImmediately([subResult = std::move(subResult)]( + GltfConverterResult&& converterResult) mutable { + if (converterResult.errors.hasErrors()) { + return subResult; + } + subResult.gltfResult = converterResult; + return subResult; + }); } - GltfConverterResult binToGltfResult = BinaryToGltfConverter::convert( - assetFuture ? byteResult.bytes : gltfData, - options, - nullptr); - result.model = std::move(binToGltfResult.model); - result.errors.merge(std::move(binToGltfResult.errors)); } // XXX If there are no scale or rotation parts to the instance transform, then @@ -500,30 +545,33 @@ void instantiateInstances( } } // namespace -GltfConverterResult I3dmToGltfConverter::convert( +CesiumAsync::Future I3dmToGltfConverter::convert( const gsl::span& instancesBinary, const CesiumGltfReader::GltfReaderOptions& options, - ConverterSubprocessor* subprocessor) { + ConverterSubprocessor* subProcessor) { GltfConverterResult result; InstancesHeader header; uint32_t headerLength = 0; parseInstancesHeader(instancesBinary, header, headerLength, result); if (result.errors) { - return result; + return subProcessor->asyncSystem.createResolvedFuture(std::move(result)); } DecodedInstances decodedInstances; - convertInstancesContent( - instancesBinary, - header, - headerLength, - options, - subprocessor, - result, - decodedInstances); - if (result.errors) { - return result; - } - instantiateInstances(result, decodedInstances); - return result; + return convertInstancesContent( + instancesBinary, + header, + headerLength, + options, + subProcessor, + result) + .thenImmediately([](ConvertResult&& convertResult) { + if (convertResult.gltfResult.errors.hasErrors()) { + return convertResult.gltfResult; + } + instantiateInstances( + convertResult.gltfResult, + convertResult.decodedInstances); + return convertResult.gltfResult; + }); } } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/LegacyUtilities.cpp b/Cesium3DTilesContent/src/LegacyUtilities.cpp index 8d2d00ce5..0a4e49e69 100644 --- a/Cesium3DTilesContent/src/LegacyUtilities.cpp +++ b/Cesium3DTilesContent/src/LegacyUtilities.cpp @@ -69,7 +69,7 @@ parseArrayValueDVec3(const rapidjson::Value& arrayValue) { } std::optional -parseArrayDValueVec3(const rapidjson::Document& document, const char* name) { +parseArrayValueDVec3(const rapidjson::Document& document, const char* name) { const auto arrayIt = document.FindMember(name); if (arrayIt != document.MemberEnd()) { return parseArrayValueDVec3(arrayIt->value); diff --git a/Cesium3DTilesContent/src/PntsToGltfConverter.cpp b/Cesium3DTilesContent/src/PntsToGltfConverter.cpp index eaca77fd7..6673f2029 100644 --- a/Cesium3DTilesContent/src/PntsToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/PntsToGltfConverter.cpp @@ -1,5 +1,6 @@ #include "BatchTableToGltfStructuralMetadata.h" +#include #include #include #include @@ -1617,19 +1618,18 @@ void convertPntsContentToGltf( } } // namespace -GltfConverterResult PntsToGltfConverter::convert( +CesiumAsync::Future PntsToGltfConverter::convert( const gsl::span& pntsBinary, const CesiumGltfReader::GltfReaderOptions& /*options*/, - ConverterSubprocessor*) { + ConverterSubprocessor* subprocessor) { GltfConverterResult result; PntsHeader header; uint32_t headerLength = 0; parsePntsHeader(pntsBinary, header, headerLength, result); - if (result.errors) { - return result; + if (!result.errors) { + convertPntsContentToGltf(pntsBinary, header, headerLength, result); } - convertPntsContentToGltf(pntsBinary, header, headerLength, result); - return result; + return subprocessor->asyncSystem.createResolvedFuture(std::move(result)); } } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp index d3cd63b0d..1b35c1da2 100644 --- a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp @@ -104,11 +104,16 @@ CesiumAsync::Future requestTileContent( const std::string& tileUrl, const std::vector& requestHeaders, CesiumGltf::Ktx2TranscodeTargets ktx2TranscodeTargets, - bool applyTextureTransform) { + bool applyTextureTransform, + const glm::dmat4& tileTransform) { return pAssetAccessor->get(asyncSystem, tileUrl, requestHeaders) .thenInWorkerThread([pLogger, ktx2TranscodeTargets, - applyTextureTransform]( + applyTextureTransform, + &asyncSystem, + pAssetAccessor, + tileTransform, + requestHeaders]( std::shared_ptr&& pCompletedRequest) mutable { const CesiumAsync::IAssetResponse* pResponse = @@ -119,8 +124,8 @@ CesiumAsync::Future requestTileContent( pLogger, "Did not receive a valid response for tile content {}", tileUrl); - return TileLoadResult::createFailedResult( - std::move(pCompletedRequest)); + return asyncSystem.createResolvedFuture( + TileLoadResult::createFailedResult(std::move(pCompletedRequest))); } uint16_t statusCode = pResponse->statusCode(); @@ -130,8 +135,8 @@ CesiumAsync::Future requestTileContent( "Received status code {} for tile content {}", statusCode, tileUrl); - return TileLoadResult::createFailedResult( - std::move(pCompletedRequest)); + return asyncSystem.createResolvedFuture( + TileLoadResult::createFailedResult(std::move(pCompletedRequest))); } // find gltf converter @@ -147,29 +152,36 @@ CesiumAsync::Future requestTileContent( CesiumGltfReader::GltfReaderOptions gltfOptions; gltfOptions.ktx2TranscodeTargets = ktx2TranscodeTargets; gltfOptions.applyTextureTransform = applyTextureTransform; - GltfConverterResult result = - converter(responseData, gltfOptions, nullptr); // XXX - - // Report any errors if there are any - logTileLoadResult(pLogger, tileUrl, result.errors); - if (result.errors || !result.model) { - return TileLoadResult::createFailedResult( - std::move(pCompletedRequest)); - } - - return TileLoadResult{ - std::move(*result.model), - CesiumGeometry::Axis::Y, - std::nullopt, - std::nullopt, - std::nullopt, - std::move(pCompletedRequest), - {}, - TileLoadResultState::Success}; + ConverterSubprocessor subprocessor{ + asyncSystem, + pAssetAccessor, + tileUrl, + tileTransform, + requestHeaders}; + return converter(responseData, gltfOptions, &subprocessor) + .thenImmediately([pLogger, tileUrl, pCompletedRequest]( + GltfConverterResult&& result) { + // Report any errors if there are any + logTileLoadResult(pLogger, tileUrl, result.errors); + if (result.errors || !result.model) { + return TileLoadResult::createFailedResult( + std::move(pCompletedRequest)); + } + + return TileLoadResult{ + std::move(*result.model), + CesiumGeometry::Axis::Y, + std::nullopt, + std::nullopt, + std::nullopt, + std::move(pCompletedRequest), + {}, + TileLoadResultState::Success}; + }); } - // content type is not supported - return TileLoadResult::createFailedResult(std::move(pCompletedRequest)); + return asyncSystem.createResolvedFuture( + TileLoadResult::createFailedResult(std::move(pCompletedRequest))); }); } } // namespace @@ -262,7 +274,8 @@ ImplicitOctreeLoader::loadTileContent(const TileLoadInput& loadInput) { tileUrl, requestHeaders, contentOptions.ktx2TranscodeTargets, - contentOptions.applyTextureTransform); + contentOptions.applyTextureTransform, + tile.getTransform()); } TileChildrenResult ImplicitOctreeLoader::createTileChildren(const Tile& tile) { diff --git a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp index d340ece83..e9331d132 100644 --- a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp @@ -114,23 +114,31 @@ CesiumAsync::Future requestTileContent( const std::string& tileUrl, const std::vector& requestHeaders, CesiumGltf::Ktx2TranscodeTargets ktx2TranscodeTargets, - bool applyTextureTransform) { + bool applyTextureTransform, + const glm::dmat4& tileTransform) { return pAssetAccessor->get(asyncSystem, tileUrl, requestHeaders) .thenInWorkerThread([pLogger, ktx2TranscodeTargets, - applyTextureTransform]( + applyTextureTransform, + &asyncSystem, + pAssetAccessor, + tileTransform, + requestHeaders]( std::shared_ptr&& pCompletedRequest) mutable { const CesiumAsync::IAssetResponse* pResponse = pCompletedRequest->response(); + auto fail = [&]() { + return asyncSystem.createResolvedFuture( + TileLoadResult::createFailedResult(std::move(pCompletedRequest))); + }; const std::string& tileUrl = pCompletedRequest->url(); if (!pResponse) { SPDLOG_LOGGER_ERROR( pLogger, "Did not receive a valid response for tile content {}", tileUrl); - return TileLoadResult::createFailedResult( - std::move(pCompletedRequest)); + return fail(); } uint16_t statusCode = pResponse->statusCode(); @@ -140,8 +148,7 @@ CesiumAsync::Future requestTileContent( "Received status code {} for tile content {}", statusCode, tileUrl); - return TileLoadResult::createFailedResult( - std::move(pCompletedRequest)); + return fail(); } // find gltf converter @@ -157,29 +164,35 @@ CesiumAsync::Future requestTileContent( CesiumGltfReader::GltfReaderOptions gltfOptions; gltfOptions.ktx2TranscodeTargets = ktx2TranscodeTargets; gltfOptions.applyTextureTransform = applyTextureTransform; - GltfConverterResult result = - converter(responseData, gltfOptions, nullptr); // XXX - - // Report any errors if there are any - logTileLoadResult(pLogger, tileUrl, result.errors); - if (result.errors || !result.model) { - return TileLoadResult::createFailedResult( - std::move(pCompletedRequest)); - } - - return TileLoadResult{ - std::move(*result.model), - CesiumGeometry::Axis::Y, - std::nullopt, - std::nullopt, - std::nullopt, - std::move(pCompletedRequest), - {}, - TileLoadResultState::Success}; + ConverterSubprocessor subprocessor{ + asyncSystem, + pAssetAccessor, + tileUrl, + tileTransform, + requestHeaders}; + return converter(responseData, gltfOptions, &subprocessor) + .thenImmediately([pLogger, tileUrl, pCompletedRequest]( + GltfConverterResult&& result) { + // Report any errors if there are any + logTileLoadResult(pLogger, tileUrl, result.errors); + if (result.errors || !result.model) { + return TileLoadResult::createFailedResult( + std::move(pCompletedRequest)); + } + + return TileLoadResult{ + std::move(*result.model), + CesiumGeometry::Axis::Y, + std::nullopt, + std::nullopt, + std::nullopt, + std::move(pCompletedRequest), + {}, + TileLoadResultState::Success}; + }); } - // content type is not supported - return TileLoadResult::createFailedResult(std::move(pCompletedRequest)); + return fail(); }); } } // namespace @@ -301,7 +314,8 @@ ImplicitQuadtreeLoader::loadTileContent(const TileLoadInput& loadInput) { tileUrl, requestHeaders, contentOptions.ktx2TranscodeTargets, - contentOptions.applyTextureTransform); + contentOptions.applyTextureTransform, + tile.getTransform()); } TileChildrenResult diff --git a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp index dc54f98ee..78e42d9fc 100644 --- a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp +++ b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp @@ -860,89 +860,90 @@ TilesetJsonLoader::loadTileContent(const TileLoadInput& loadInput) { std::string resolvedUrl = CesiumUtility::Uri::resolve(this->_baseUrl, *url, true); return pAssetAccessor->get(asyncSystem, resolvedUrl, requestHeaders) - .thenInWorkerThread( - [pLogger, - contentOptions, - tileTransform, - tileRefine, - upAxis = _upAxis, - externalContentInitializer = std::move(externalContentInitializer), - pAssetAccessor, - asyncSystem, - requestHeaders](std::shared_ptr&& - pCompletedRequest) mutable { - auto pResponse = pCompletedRequest->response(); - const std::string& tileUrl = pCompletedRequest->url(); - if (!pResponse) { - SPDLOG_LOGGER_ERROR( - pLogger, - "Did not receive a valid response for tile content {}", - tileUrl); - return TileLoadResult::createFailedResult( - std::move(pCompletedRequest)); - } - - uint16_t statusCode = pResponse->statusCode(); - if (statusCode != 0 && (statusCode < 200 || statusCode >= 300)) { - SPDLOG_LOGGER_ERROR( - pLogger, - "Received status code {} for tile content {}", - statusCode, - tileUrl); - return TileLoadResult::createFailedResult( - std::move(pCompletedRequest)); - } - - // find gltf converter - const auto& responseData = pResponse->data(); - auto converter = GltfConverters::getConverterByMagic(responseData); - if (!converter) { - converter = GltfConverters::getConverterByFileExtension(tileUrl); - } - - if (converter) { - // Convert to gltf - ConverterSubprocessor subprocessor{ - asyncSystem, - pAssetAccessor, - tileUrl, - tileTransform, - requestHeaders}; - CesiumGltfReader::GltfReaderOptions gltfOptions; - gltfOptions.ktx2TranscodeTargets = - contentOptions.ktx2TranscodeTargets; - gltfOptions.applyTextureTransform = - contentOptions.applyTextureTransform; - GltfConverterResult result = - converter(responseData, gltfOptions, &subprocessor); - - // Report any errors if there are any - logTileLoadResult(pLogger, tileUrl, result.errors); - if (result.errors) { - return TileLoadResult::createFailedResult( - std::move(pCompletedRequest)); - } - - return TileLoadResult{ - std::move(*result.model), - upAxis, - std::nullopt, - std::nullopt, - std::nullopt, - std::move(pCompletedRequest), - {}, - TileLoadResultState::Success}; - } else { - // not a renderable content, then it must be external tileset - return parseExternalTilesetInWorkerThread( + .thenInWorkerThread([pLogger, + contentOptions, + tileTransform, + tileRefine, + upAxis = _upAxis, + externalContentInitializer = + std::move(externalContentInitializer), + pAssetAccessor, + &asyncSystem, + requestHeaders]( + std::shared_ptr&& + pCompletedRequest) mutable { + auto pResponse = pCompletedRequest->response(); + const std::string& tileUrl = pCompletedRequest->url(); + if (!pResponse) { + SPDLOG_LOGGER_ERROR( + pLogger, + "Did not receive a valid response for tile content {}", + tileUrl); + return asyncSystem.createResolvedFuture( + TileLoadResult::createFailedResult(std::move(pCompletedRequest))); + } + + uint16_t statusCode = pResponse->statusCode(); + if (statusCode != 0 && (statusCode < 200 || statusCode >= 300)) { + SPDLOG_LOGGER_ERROR( + pLogger, + "Received status code {} for tile content {}", + statusCode, + tileUrl); + return asyncSystem.createResolvedFuture( + TileLoadResult::createFailedResult(std::move(pCompletedRequest))); + } + + // find gltf converter + const auto& responseData = pResponse->data(); + auto converter = GltfConverters::getConverterByMagic(responseData); + if (!converter) { + converter = GltfConverters::getConverterByFileExtension(tileUrl); + } + + if (converter) { + // Convert to gltf + ConverterSubprocessor subprocessor{ + asyncSystem, + pAssetAccessor, + tileUrl, + tileTransform, + requestHeaders}; + CesiumGltfReader::GltfReaderOptions gltfOptions; + gltfOptions.ktx2TranscodeTargets = + contentOptions.ktx2TranscodeTargets; + gltfOptions.applyTextureTransform = + contentOptions.applyTextureTransform; + return converter(responseData, gltfOptions, &subprocessor) + .thenImmediately([pLogger, upAxis, tileUrl, pCompletedRequest]( + GltfConverterResult&& result) { + logTileLoadResult(pLogger, tileUrl, result.errors); + if (result.errors) { + return TileLoadResult::createFailedResult( + std::move(pCompletedRequest)); + } + return TileLoadResult{ + std::move(*result.model), + upAxis, + std::nullopt, + std::nullopt, + std::nullopt, + std::move(pCompletedRequest), + {}, + TileLoadResultState::Success}; + }); + } else { + // not a renderable content, then it must be external tileset + return asyncSystem.createResolvedFuture( + parseExternalTilesetInWorkerThread( tileTransform, upAxis, tileRefine, pLogger, std::move(pCompletedRequest), - std::move(externalContentInitializer)); - } - }); + std::move(externalContentInitializer))); + } + }); } TileChildrenResult TilesetJsonLoader::createTileChildren(const Tile& tile) { From 81bb2ff1bdeecbba956782f6b0c2660ac216465e Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Wed, 17 Apr 2024 16:55:35 +0200 Subject: [PATCH 10/32] Change ConverterSubprocessor arguments from pointer to reference It's required now. --- .../B3dmToGltfConverter.h | 2 +- .../BinaryToGltfConverter.h | 4 +-- .../CmptToGltfConverter.h | 2 +- .../Cesium3DTilesContent/GltfConverters.h | 6 ++-- .../I3dmToGltfConverter.h | 2 +- .../PntsToGltfConverter.h | 2 +- .../src/B3dmToGltfConverter.cpp | 8 ++--- .../src/BinaryToGltfConverter.cpp | 6 ++-- .../src/CmptToGltfConverter.cpp | 14 ++++---- Cesium3DTilesContent/src/GltfConverters.cpp | 8 ++--- .../src/I3dmToGltfConverter.cpp | 33 +++++++++---------- .../src/PntsToGltfConverter.cpp | 4 +-- .../src/ImplicitOctreeLoader.cpp | 2 +- .../src/ImplicitQuadtreeLoader.cpp | 2 +- .../src/TilesetJsonLoader.cpp | 2 +- 15 files changed, 48 insertions(+), 49 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/B3dmToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/B3dmToGltfConverter.h index f45e4428e..69be1ec1f 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/B3dmToGltfConverter.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/B3dmToGltfConverter.h @@ -17,6 +17,6 @@ struct B3dmToGltfConverter { static CesiumAsync::Future convert( const gsl::span& b3dmBinary, const CesiumGltfReader::GltfReaderOptions& options, - ConverterSubprocessor* subprocessor); + const ConverterSubprocessor& subprocessor); }; } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h index c4c5da6e8..08ad61350 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h @@ -16,13 +16,13 @@ struct BinaryToGltfConverter { static CesiumAsync::Future convert( const gsl::span& gltfBinary, const CesiumGltfReader::GltfReaderOptions& options, - ConverterSubprocessor* subProcessor); + const ConverterSubprocessor& subProcessor); private: static GltfConverterResult convertImmediate( const gsl::span& gltfBinary, const CesiumGltfReader::GltfReaderOptions& options, - ConverterSubprocessor* subProcessor); + const ConverterSubprocessor& subProcessor); static CesiumGltfReader::GltfReader _gltfReader; }; } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/CmptToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/CmptToGltfConverter.h index 2d02f3ef3..7c39f35f8 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/CmptToGltfConverter.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/CmptToGltfConverter.h @@ -15,6 +15,6 @@ struct CmptToGltfConverter { static CesiumAsync::Future convert( const gsl::span& cmptBinary, const CesiumGltfReader::GltfReaderOptions& options, - ConverterSubprocessor*); + const ConverterSubprocessor&); }; } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h index e45d9dc91..802349468 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h @@ -61,7 +61,7 @@ class CESIUM3DTILESCONTENT_API GltfConverters { using ConverterFunction = CesiumAsync::Future (*)( const gsl::span& content, const CesiumGltfReader::GltfReaderOptions& options, - ConverterSubprocessor* subprocessor); + const ConverterSubprocessor& subprocessor); /** * @brief Register the given function for the given magic header. @@ -151,7 +151,7 @@ class CESIUM3DTILESCONTENT_API GltfConverters { const std::string& filePath, const gsl::span& content, const CesiumGltfReader::GltfReaderOptions& options, - ConverterSubprocessor* subprocessor); + const ConverterSubprocessor& subprocessor); /** * @brief Creates the {@link GltfConverterResult} from the given @@ -176,7 +176,7 @@ class CESIUM3DTILESCONTENT_API GltfConverters { static CesiumAsync::Future convert( const gsl::span& content, const CesiumGltfReader::GltfReaderOptions& options, - ConverterSubprocessor* subprocessor); + const ConverterSubprocessor& subprocessor); private: static std::string toLowerCase(const std::string_view& str); diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/I3dmToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/I3dmToGltfConverter.h index 0d15ad314..698e6144a 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/I3dmToGltfConverter.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/I3dmToGltfConverter.h @@ -17,6 +17,6 @@ struct I3dmToGltfConverter { static CesiumAsync::Future convert( const gsl::span& instancesBinary, const CesiumGltfReader::GltfReaderOptions& options, - ConverterSubprocessor* subprocessor); + const ConverterSubprocessor& subprocessor); }; } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/PntsToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/PntsToGltfConverter.h index 61fd763f7..4215194f1 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/PntsToGltfConverter.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/PntsToGltfConverter.h @@ -17,6 +17,6 @@ struct PntsToGltfConverter { static CesiumAsync::Future convert( const gsl::span& pntsBinary, const CesiumGltfReader::GltfReaderOptions& options, - ConverterSubprocessor* subprocessor); + const ConverterSubprocessor& subprocessor); }; } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/B3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/B3dmToGltfConverter.cpp index 140514c93..a73bc59d3 100644 --- a/Cesium3DTilesContent/src/B3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/B3dmToGltfConverter.cpp @@ -119,7 +119,7 @@ CesiumAsync::Future convertB3dmContentToGltf( const B3dmHeader& header, uint32_t headerLength, const CesiumGltfReader::GltfReaderOptions& options, - ConverterSubprocessor* subprocessor) { + const ConverterSubprocessor& subprocessor) { const uint32_t glbStart = headerLength + header.featureTableJsonByteLength + header.featureTableBinaryByteLength + header.batchTableJsonByteLength + @@ -131,7 +131,7 @@ CesiumAsync::Future 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 subprocessor.asyncSystem.createResolvedFuture(std::move(result)); } const gsl::span glbData = @@ -230,13 +230,13 @@ void convertB3dmMetadataToGltfStructuralMetadata( CesiumAsync::Future B3dmToGltfConverter::convert( const gsl::span& b3dmBinary, const CesiumGltfReader::GltfReaderOptions& options, - ConverterSubprocessor* subprocessor) { + const ConverterSubprocessor& subprocessor) { GltfConverterResult result; B3dmHeader header; uint32_t headerLength = 0; parseB3dmHeader(b3dmBinary, header, headerLength, result); if (result.errors) { - return subprocessor->asyncSystem.createResolvedFuture(std::move(result)); + return subprocessor.asyncSystem.createResolvedFuture(std::move(result)); } return convertB3dmContentToGltf( diff --git a/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp b/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp index c3dfd83d2..a6cb23f1a 100644 --- a/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp @@ -7,7 +7,7 @@ CesiumGltfReader::GltfReader BinaryToGltfConverter::_gltfReader; GltfConverterResult BinaryToGltfConverter::convertImmediate( const gsl::span& gltfBinary, const CesiumGltfReader::GltfReaderOptions& options, - ConverterSubprocessor*) { + const ConverterSubprocessor&) { CesiumGltfReader::GltfReaderResult loadedGltf = _gltfReader.readGltf(gltfBinary, options); @@ -21,8 +21,8 @@ GltfConverterResult BinaryToGltfConverter::convertImmediate( CesiumAsync::Future BinaryToGltfConverter::convert( const gsl::span& gltfBinary, const CesiumGltfReader::GltfReaderOptions& options, - ConverterSubprocessor* subprocessor) { - return subprocessor->asyncSystem.createResolvedFuture( + const ConverterSubprocessor& subprocessor) { + return subprocessor.asyncSystem.createResolvedFuture( convertImmediate(gltfBinary, options, subprocessor)); } } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/CmptToGltfConverter.cpp b/Cesium3DTilesContent/src/CmptToGltfConverter.cpp index bd35c5d1a..f7e7f076c 100644 --- a/Cesium3DTilesContent/src/CmptToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/CmptToGltfConverter.cpp @@ -25,11 +25,11 @@ static_assert(sizeof(InnerHeader) == 12); CesiumAsync::Future CmptToGltfConverter::convert( const gsl::span& cmptBinary, const CesiumGltfReader::GltfReaderOptions& options, - ConverterSubprocessor* subProcessor) { + const ConverterSubprocessor& subProcessor) { 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 subProcessor.asyncSystem.createResolvedFuture(std::move(result)); } const CmptHeader* pHeader = @@ -37,14 +37,14 @@ CesiumAsync::Future 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 subProcessor.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 subProcessor.asyncSystem.createResolvedFuture(std::move(result)); } if (pHeader->byteLength > cmptBinary.size()) { @@ -52,7 +52,7 @@ CesiumAsync::Future CmptToGltfConverter::convert( "Composite tile byteLength is {} but only {} bytes are available.", pHeader->byteLength, cmptBinary.size())); - return subProcessor->asyncSystem.createResolvedFuture(std::move(result)); + return subProcessor.asyncSystem.createResolvedFuture(std::move(result)); } std::vector> innerTiles; @@ -90,10 +90,10 @@ CesiumAsync::Future CmptToGltfConverter::convert( "Composite tile does not contain any loadable inner " "tiles."); } - return subProcessor->asyncSystem.createResolvedFuture(std::move(result)); + return subProcessor.asyncSystem.createResolvedFuture(std::move(result)); } - return subProcessor->asyncSystem.all(std::move(innerTiles)) + return subProcessor.asyncSystem.all(std::move(innerTiles)) .thenImmediately([](std::vector&& innerResults) { if (innerResults.size() == 1) { return innerResults[0]; diff --git a/Cesium3DTilesContent/src/GltfConverters.cpp b/Cesium3DTilesContent/src/GltfConverters.cpp index 02423b836..c01bb71c0 100644 --- a/Cesium3DTilesContent/src/GltfConverters.cpp +++ b/Cesium3DTilesContent/src/GltfConverters.cpp @@ -41,7 +41,7 @@ CesiumAsync::Future GltfConverters::convert( const std::string& filePath, const gsl::span& content, const CesiumGltfReader::GltfReaderOptions& options, - ConverterSubprocessor* subprocessor) { + const ConverterSubprocessor& subprocessor) { std::string magic; auto converterFun = getConverterByMagic(content, magic); if (converterFun) { @@ -61,14 +61,14 @@ CesiumAsync::Future GltfConverters::convert( fileExtension, magic)); - return subprocessor->asyncSystem.createResolvedFuture( + return subprocessor.asyncSystem.createResolvedFuture( GltfConverterResult{std::nullopt, std::move(errors)}); } CesiumAsync::Future GltfConverters::convert( const gsl::span& content, const CesiumGltfReader::GltfReaderOptions& options, - ConverterSubprocessor* subprocessor) { + const ConverterSubprocessor& subprocessor) { std::string magic; auto converter = getConverterByMagic(content, magic); if (converter) { @@ -80,7 +80,7 @@ CesiumAsync::Future GltfConverters::convert( "No loader registered for tile with magic value '{}'", magic)); - return subprocessor->asyncSystem.createResolvedFuture( + return subprocessor.asyncSystem.createResolvedFuture( GltfConverterResult{std::nullopt, std::move(errors)}); } diff --git a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp index 4aa41acf4..a07ea29a7 100644 --- a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp @@ -187,14 +187,17 @@ CesiumAsync::Future convertInstancesContent( const InstancesHeader& header, uint32_t headerLength, const CesiumGltfReader::GltfReaderOptions& options, - ConverterSubprocessor* subprocessor, + 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 subprocessor->asyncSystem.createResolvedFuture(std::move(subResult)); + return finishEarly(); } const uint32_t glTFStart = headerLength + header.featureTableJsonByteLength + header.featureTableBinaryByteLength + @@ -215,7 +218,7 @@ CesiumAsync::Future convertInstancesContent( "{}", featureTableJson.GetParseError(), featureTableJson.GetErrorOffset())); - return subprocessor->asyncSystem.createResolvedFuture(std::move(subResult)); + return finishEarly(); } InstanceContent parsedContent; // Global semantics @@ -226,7 +229,7 @@ CesiumAsync::Future convertInstancesContent( subResult.gltfResult.errors.emplaceError( "Error parsing I3DM feature table, no valid " "INSTANCES_LENGTH was found."); - return subprocessor->asyncSystem.createResolvedFuture(std::move(subResult)); + return finishEarly(); } if (auto optRtcCenter = parseArrayValueDVec3(featureTableJson, "RTC_CENTER")) { @@ -236,16 +239,14 @@ CesiumAsync::Future convertInstancesContent( parseOffset(featureTableJson, "POSITION", subResult.gltfResult.errors); if (!parsedContent.position) { if (subResult.gltfResult.errors.hasErrors()) { - return subprocessor->asyncSystem.createResolvedFuture( - std::move(subResult)); + return finishEarly(); } parsedContent.positionQuantized = parseOffset( featureTableJson, "POSITION_QUANTIZED", subResult.gltfResult.errors); if (subResult.gltfResult.errors.hasErrors()) { - return subprocessor->asyncSystem.createResolvedFuture( - std::move(subResult)); + return finishEarly(); } } if (parsedContent.positionQuantized) { @@ -255,8 +256,7 @@ CesiumAsync::Future convertInstancesContent( subResult.gltfResult.errors.emplaceError( "Error parsing I3DM feature table, No valid " "QUANTIZED_VOLUME_OFFSET property"); - return subprocessor->asyncSystem.createResolvedFuture( - std::move(subResult)); + return finishEarly(); } parsedContent.quantizedVolumeScale = parseArrayValueDVec3(featureTableJson, "QUANTIZED_VOLUME_SCALE"); @@ -264,8 +264,7 @@ CesiumAsync::Future convertInstancesContent( subResult.gltfResult.errors.emplaceError( "Error parsing I3DM feature table, No valid " "QUANTIZED_VOLUME_SCALE property"); - return subprocessor->asyncSystem.createResolvedFuture( - std::move(subResult)); + return finishEarly(); } } decodedInstances.rotationENU = false; @@ -296,7 +295,7 @@ CesiumAsync::Future convertInstancesContent( parsedContent.batchId = parseOffset(featureTableJson, "BATCH_ID", subResult.gltfResult.errors); if (subResult.gltfResult.errors.hasErrors()) { - return subprocessor->asyncSystem.createResolvedFuture(std::move(subResult)); + return finishEarly(); } auto featureTableBinaryData = instancesBinary.subspan( headerLength + header.featureTableJsonByteLength, @@ -372,14 +371,14 @@ CesiumAsync::Future convertInstancesContent( auto gltfUri = std::string( reinterpret_cast(gltfData.data()), gltfData.size()); - return get(*subprocessor, gltfUri) + return get(subprocessor, gltfUri) .thenImmediately( [options, subprocessor](ByteResult&& byteResult) -> CesiumAsync::Future { if (byteResult.errorList.hasErrors()) { GltfConverterResult errorResult; errorResult.errors.merge(byteResult.errorList); - return subprocessor->asyncSystem.createResolvedFuture( + return subprocessor.asyncSystem.createResolvedFuture( std::move(errorResult)); } return BinaryToGltfConverter::convert( @@ -548,13 +547,13 @@ void instantiateInstances( CesiumAsync::Future I3dmToGltfConverter::convert( const gsl::span& instancesBinary, const CesiumGltfReader::GltfReaderOptions& options, - ConverterSubprocessor* subProcessor) { + const ConverterSubprocessor& subProcessor) { GltfConverterResult result; InstancesHeader header; uint32_t headerLength = 0; parseInstancesHeader(instancesBinary, header, headerLength, result); if (result.errors) { - return subProcessor->asyncSystem.createResolvedFuture(std::move(result)); + return subProcessor.asyncSystem.createResolvedFuture(std::move(result)); } DecodedInstances decodedInstances; return convertInstancesContent( diff --git a/Cesium3DTilesContent/src/PntsToGltfConverter.cpp b/Cesium3DTilesContent/src/PntsToGltfConverter.cpp index 6673f2029..372f4baec 100644 --- a/Cesium3DTilesContent/src/PntsToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/PntsToGltfConverter.cpp @@ -1621,7 +1621,7 @@ void convertPntsContentToGltf( CesiumAsync::Future PntsToGltfConverter::convert( const gsl::span& pntsBinary, const CesiumGltfReader::GltfReaderOptions& /*options*/, - ConverterSubprocessor* subprocessor) { + const ConverterSubprocessor& subprocessor) { GltfConverterResult result; PntsHeader header; uint32_t headerLength = 0; @@ -1630,6 +1630,6 @@ CesiumAsync::Future PntsToGltfConverter::convert( convertPntsContentToGltf(pntsBinary, header, headerLength, result); } - return subprocessor->asyncSystem.createResolvedFuture(std::move(result)); + return subprocessor.asyncSystem.createResolvedFuture(std::move(result)); } } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp index 1b35c1da2..b22dd49b6 100644 --- a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp @@ -158,7 +158,7 @@ CesiumAsync::Future requestTileContent( tileUrl, tileTransform, requestHeaders}; - return converter(responseData, gltfOptions, &subprocessor) + return converter(responseData, gltfOptions, subprocessor) .thenImmediately([pLogger, tileUrl, pCompletedRequest]( GltfConverterResult&& result) { // Report any errors if there are any diff --git a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp index e9331d132..84455218a 100644 --- a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp @@ -170,7 +170,7 @@ CesiumAsync::Future requestTileContent( tileUrl, tileTransform, requestHeaders}; - return converter(responseData, gltfOptions, &subprocessor) + return converter(responseData, gltfOptions, subprocessor) .thenImmediately([pLogger, tileUrl, pCompletedRequest]( GltfConverterResult&& result) { // Report any errors if there are any diff --git a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp index 78e42d9fc..3f5266312 100644 --- a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp +++ b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp @@ -914,7 +914,7 @@ TilesetJsonLoader::loadTileContent(const TileLoadInput& loadInput) { contentOptions.ktx2TranscodeTargets; gltfOptions.applyTextureTransform = contentOptions.applyTextureTransform; - return converter(responseData, gltfOptions, &subprocessor) + return converter(responseData, gltfOptions, subprocessor) .thenImmediately([pLogger, upAxis, tileUrl, pCompletedRequest]( GltfConverterResult&& result) { logTileLoadResult(pLogger, tileUrl, result.errors); From 7db60d623456bdf0387e4f90896d14522e8fbd3d Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Wed, 17 Apr 2024 17:16:29 +0200 Subject: [PATCH 11/32] Add std::move in response to compiler warning --- Cesium3DTilesContent/src/B3dmToGltfConverter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cesium3DTilesContent/src/B3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/B3dmToGltfConverter.cpp index a73bc59d3..852d63a77 100644 --- a/Cesium3DTilesContent/src/B3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/B3dmToGltfConverter.cpp @@ -254,7 +254,7 @@ CesiumAsync::Future B3dmToGltfConverter::convert( headerLength, glbResult); } - return glbResult; + return std::move(glbResult); }); } } // namespace Cesium3DTilesContent From 616bf74e73484cc74ad102be2566955fbedceabd Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Fri, 19 Apr 2024 18:19:55 +0200 Subject: [PATCH 12/32] Rebase instance positions to their mean Construct an RTC_CENTER, in effect. Otherwise instance positions could be too large to render without jitter. --- .../Cesium3DTilesContent/LegacyUtilities.h | 2 + .../src/I3dmToGltfConverter.cpp | 47 ++++++++++++++++--- Cesium3DTilesContent/src/LegacyUtilities.cpp | 16 +++++++ 3 files changed, 58 insertions(+), 7 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h index 869d4ba4a..c9a95d5f3 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h @@ -98,5 +98,7 @@ int32_t createAccessorInGltf( const int32_t componentType, const int64_t count, const std::string type); + +void applyRTC(CesiumGltf::Model& gltf, const glm::dvec3& rtc); } // namespace LegacyUtilities } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp index a07ea29a7..2f48d34b5 100644 --- a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -38,9 +39,37 @@ struct DecodedInstances { std::vector rotations; std::vector scales; std::string gltfUri; - bool rotationENU; + bool rotationENU = false; + std::optional rtcCenter; }; +// 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. + +void rebaseInstances(DecodedInstances& decodedInstances) { + if (decodedInstances.positions.empty()) { + return; + } + glm::dvec3 newCenter(0.0, 0.0, 0.0); + for (const auto& pos : decodedInstances.positions) { + newCenter += glm::dvec3(pos); + } + newCenter /= static_cast(decodedInstances.positions.size()); + std::transform( + decodedInstances.positions.begin(), + decodedInstances.positions.end(), + decodedInstances.positions.begin(), + [&](const glm::vec3& pos) { + return glm::vec3(glm::dvec3(pos) - newCenter); + }); + if (decodedInstances.rtcCenter) { + newCenter += *decodedInstances.rtcCenter; + } + decodedInstances.rtcCenter = newCenter; +} + void parseInstancesHeader( const gsl::span& instancesBinary, InstancesHeader& header, @@ -75,7 +104,7 @@ void parseInstancesHeader( struct InstanceContent { uint32_t instancesLength = 0; - glm::dvec3 rtcCenter; + std::optional rtcCenter; std::optional quantizedVolumeOffset; std::optional quantizedVolumeScale; bool eastNorthUp = false; @@ -231,10 +260,10 @@ CesiumAsync::Future convertInstancesContent( "INSTANCES_LENGTH was found."); return finishEarly(); } - if (auto optRtcCenter = - parseArrayValueDVec3(featureTableJson, "RTC_CENTER")) { - parsedContent.rtcCenter = *optRtcCenter; - } + parsedContent.rtcCenter = + parseArrayValueDVec3(featureTableJson, "RTC_CENTER"); + decodedInstances.rtcCenter = parsedContent.rtcCenter; + parsedContent.position = parseOffset(featureTableJson, "POSITION", subResult.gltfResult.errors); if (!parsedContent.position) { @@ -302,7 +331,7 @@ CesiumAsync::Future convertInstancesContent( header.featureTableBinaryByteLength); decodedInstances.positions.resize( parsedContent.instancesLength, - glm::vec3(parsedContent.rtcCenter)); + glm::vec3(0.0f, 0.0f, 0.0f)); if (parsedContent.position) { const auto* rawPosition = reinterpret_cast( featureTableBinaryData.data() + *parsedContent.position); @@ -365,6 +394,7 @@ CesiumAsync::Future convertInstancesContent( decodedInstances.scales[i] = rawScaleNonUniform[i]; } } + rebaseInstances(decodedInstances); ByteResult byteResult; if (header.gltfFormat == 0) { // Need to recursively read the glTF content. @@ -538,6 +568,9 @@ void instantiateInstances( gpuExt.attributes["SCALE"] = scaleAccessorId; } }); + if (decodedInstances.rtcCenter) { + applyRTC(*result.model, *decodedInstances.rtcCenter); + } instanceBuffer.byteLength = static_cast(instanceBuffer.cesium.data.size()); instanceBufferView.byteLength = instanceBuffer.byteLength; diff --git a/Cesium3DTilesContent/src/LegacyUtilities.cpp b/Cesium3DTilesContent/src/LegacyUtilities.cpp index 0a4e49e69..50bf3703c 100644 --- a/Cesium3DTilesContent/src/LegacyUtilities.cpp +++ b/Cesium3DTilesContent/src/LegacyUtilities.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -125,6 +126,21 @@ int32_t createAccessorInGltf( return static_cast(accessorId); } +void applyRTC(Model& gltf, const glm::dvec3& rtc) { + using namespace CesiumGltfContent; + auto upToZ = GltfUtilities::applyGltfUpAxisTransform(gltf, glm::dmat4x4(1.0)); + auto rtcTransform = inverse(upToZ); + rtcTransform = translate(rtcTransform, rtc); + rtcTransform = rtcTransform * upToZ; + gltf.forEachRootNodeInScene(-1, [&](Model&, Node& node) { + auto nodeTransform = GltfUtilities::getNodeTransform(node); + if (nodeTransform) { + nodeTransform = rtcTransform * *nodeTransform; + GltfUtilities::setNodeTransform(node, *nodeTransform); + } + }); +} + } // namespace LegacyUtilities CesiumAsync::Future From 80ade662373d109fd8c21cc3c94fa96ee99ae132 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Fri, 26 Apr 2024 10:23:01 +0200 Subject: [PATCH 13/32] I3dm: Support the EAST_NORTH_UP global semantic --- .../src/I3dmToGltfConverter.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp index 2f48d34b5..dce78c4e4 100644 --- a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -376,6 +377,24 @@ CesiumAsync::Future convertInstancesContent( glm::vec3 dRight = decodeOct32P(rawRightOct[i]); decodedInstances.rotations[i] = rotationFromUpRight(dUp, dRight); } + } else if (decodedInstances.rotationENU) { + glm::dmat4 worldTransform = subprocessor.tileTransform; + if (decodedInstances.rtcCenter) { + worldTransform = translate(worldTransform, *decodedInstances.rtcCenter); + } + auto worldTransformInv = inverse(worldTransform); + for (size_t i = 0; i < decodedInstances.positions.size(); ++i) { + auto worldPos = + worldTransform * glm::dvec4(decodedInstances.positions[i], 1.0); + CesiumGeospatial::LocalHorizontalCoordinateSystem enu( + (glm::dvec3(worldPos))); + const auto& ecef = enu.getLocalToEcefTransformation(); + // back into tile coordinate system + auto tileFrame = worldTransformInv * ecef; + glm::quat tileFrameRot = + rotationFromUpRight(glm::vec3(tileFrame[1]), glm::vec3(tileFrame[0])); + decodedInstances.rotations[i] = tileFrameRot; + } } decodedInstances.scales.resize( parsedContent.instancesLength, From 9661e40ead745074030e4eccc72fb7882e700664 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Fri, 26 Apr 2024 11:19:28 +0200 Subject: [PATCH 14/32] Refactor and support existing instances in GLB Simplified the code by not trying to optimize the inclusion of rotation and scale transforms. --- .../src/I3dmToGltfConverter.cpp | 312 +++++++++++++----- 1 file changed, 231 insertions(+), 81 deletions(-) diff --git a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp index dce78c4e4..62e8a2e45 100644 --- a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp @@ -4,8 +4,11 @@ #include #include #include +#include +#include #include #include +#include #include #include @@ -457,27 +460,183 @@ CesiumAsync::Future convertInstancesContent( } } -// XXX If there are no scale or rotation parts to the instance transform, then -// there shouldn't be any after applying the forward and inverse glTF -// transforms. Should prove this! +glm::dmat4 +composeInstanceTransform(size_t i, const DecodedInstances& decodedInstances) { + glm::dmat4 result(1.0); + if (!decodedInstances.positions.empty()) { + result = translate(result, glm::dvec3(decodedInstances.positions[i])); + } + if (!decodedInstances.rotations.empty()) { + result = result * toMat4(glm::dquat(decodedInstances.rotations[i])); + } + if (!decodedInstances.scales.empty()) { + result = scale(result, glm::dvec3(decodedInstances.scales[i])); + } + return result; +} + +// Helpers for different instancing rotation formats + +template struct is_float_quat : std::false_type {}; + +template <> +struct is_float_quat> : std::true_type { +}; + +template struct is_int_quat : std::false_type {}; + +template +struct is_int_quat> + : std::conjunction, std::is_signed> {}; + +template +inline constexpr bool is_float_quat_v = is_float_quat::value; + +template +inline constexpr bool is_int_quat_v = is_int_quat::value; + +std::vector meshGpuInstances( + GltfConverterResult& result, + const ExtensionExtMeshGpuInstancing& gpuExt) { + const Model& model = *result.model; + std::vector instances; + if (gpuExt.attributes.empty()) { + return instances; + } + auto getInstanceAccessor = [&](const char* name) -> const Accessor* { + if (auto accessorItr = gpuExt.attributes.find(name); + accessorItr != gpuExt.attributes.end()) { + return Model::getSafe(&model.accessors, accessorItr->second); + } + return nullptr; + }; + auto errorOut = [&](const std::string& errorMsg) { + result.errors.emplaceError(errorMsg); + return instances; + }; + + const Accessor* translations = getInstanceAccessor("TRANSLATION"); + const Accessor* rotations = getInstanceAccessor("ROTATION"); + const Accessor* scales = getInstanceAccessor("SCALE"); + int64_t count = 0; + if (translations) { + count = translations->count; + } + if (rotations) { + if (count == 0) { + count = rotations->count; + } else if (count != rotations->count) { + return errorOut(fmt::format( + "instance rotation count {} not consistent with {}", + rotations->count, + count)); + } + } + if (scales) { + if (count == 0) { + count = scales->count; + } else if (count != scales->count) { + return errorOut(fmt::format( + "instance scale count {} not consistent with {}", + scales->count, + count)); + } + } + if (count == 0) { + return errorOut("No valid instance data"); + } + instances.resize(static_cast(count), glm::dmat4(1.0)); + if (translations) { + AccessorView> translationAccessor( + model, + *translations); + if (translationAccessor.status() == AccessorViewStatus::Valid) { + for (unsigned i = 0; i < count; ++i) { + glm::dvec3 transVec( + translationAccessor[i].value[0], + translationAccessor[i].value[1], + translationAccessor[i].value[2]); + instances[i] = glm::translate(instances[i], transVec); + } + } else { + return errorOut("invalid accessor for instance translations"); + } + } + if (rotations) { + createAccessorView(model, *rotations, [&](auto&& quatView) -> void { + using QuatType = decltype(quatView[0]); + using ValueType = std::decay_t; + if constexpr (is_float_quat_v) { + for (unsigned i = 0; i < count; ++i) { + glm::dquat quat( + quatView[i].value[3], + quatView[i].value[0], + quatView[i].value[1], + quatView[i].value[2]); + instances[i] = instances[i] * glm::toMat4(quat); + } + } else if constexpr (is_int_quat_v) { + for (unsigned i = 0; i < count; ++i) { + float val[4]; + for (unsigned j = 0; j < 4; ++j) { + val[j] = static_cast(normalize(quatView[i].value[j])); + } + glm::dquat quat(val[3], val[0], val[1], val[2]); + instances[i] = instances[i] * glm::toMat4(quat); + } + } + }); + } + if (scales) { + AccessorView> scaleAccessor( + model, + *scales); + if (scaleAccessor.status() == AccessorViewStatus::Valid) { + for (unsigned i = 0; i < count; ++i) { + glm::dvec3 scaleFactors( + scaleAccessor[i].value[0], + scaleAccessor[i].value[1], + scaleAccessor[i].value[2]); + instances[i] = glm::scale(instances[i], scaleFactors); + } + } else { + return errorOut("invalid accessor for instance translations"); + } + } + return instances; +} + +const size_t rotOffset = sizeof(glm::vec3); +const size_t scaleOffset = rotOffset + sizeof(glm::quat); +const size_t totalStride = + sizeof(glm::vec3) + sizeof(glm::quat) + sizeof(glm::vec3); + +void copyToBuffer( + const glm::dvec3& position, + const glm::dquat& rotation, + const glm::dvec3& scale, + std::byte* bufferLoc) { + glm::vec3 fposition(position); + std::memcpy(bufferLoc, &fposition, sizeof(fposition)); + glm::quat frotation(rotation); + std::memcpy(bufferLoc + rotOffset, &frotation, sizeof(frotation)); + glm::vec3 fscale(scale); + std::memcpy(bufferLoc + scaleOffset, &fscale, sizeof(fscale)); +} + +void copyToBuffer( + const glm::dvec3& position, + const glm::dquat& rotation, + const glm::dvec3& scale, + std::vector& bufferData, + size_t i) { + copyToBuffer(position, rotation, scale, &bufferData[i * totalStride]); +} void instantiateInstances( GltfConverterResult& result, const DecodedInstances& decodedInstances) { std::set meshNodes; - size_t totalStride = sizeof(float) * 3; - size_t rotOffset = 0; - const bool hasRotations = !decodedInstances.rotations.empty(); - if (hasRotations) { - rotOffset = sizeof(float) * 3; - totalStride += sizeof(float) * 4; - } - size_t scaleOffset = 0; - const bool hasScales = !decodedInstances.scales.empty(); - if (hasScales) { - scaleOffset = totalStride; - totalStride += sizeof(float) * 3; - } int32_t instanceBufferId = createBufferInGltf(*result.model); auto& instanceBuffer = result.model->buffers[static_cast(instanceBufferId)]; @@ -490,7 +649,6 @@ void instantiateInstances( result.model->bufferViews[static_cast(instanceBufferViewId)]; const auto numInstances = static_cast(decodedInstances.positions.size()); - const size_t instanceDataSize = totalStride * numInstances; auto upToZ = CesiumGltfContent::GltfUtilities::applyGltfUpAxisTransform( *result.model, glm::dmat4x4(1.0)); @@ -505,87 +663,79 @@ void instantiateInstances( if (!notSeen) { return; } + std::vector existingInstances{glm::dmat4(1.0)}; + auto& gpuExt = node.addExtension(); + 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); + if (numInstances * existingInstances.size() > + std::numeric_limits::max()) { + result.errors.emplaceError(fmt::format( + "Too many instances: {} from i3dm and {} from glb", + numInstances, + existingInstances.size())); + return; + } + } + const uint32_t numNewInstances = + static_cast(numInstances * existingInstances.size()); + const size_t instanceDataSize = totalStride * numNewInstances; auto dataBaseOffset = static_cast(instanceBuffer.cesium.data.size()); instanceBuffer.cesium.data.resize(dataBaseOffset + instanceDataSize); // Transform instance transform into local glTF coordinate system. const auto toTile = upToZ * transform; const auto toTileInv = inverse(toTile); + size_t destInstanceIndx = 0; for (unsigned i = 0; i < numInstances; ++i) { - auto instMat = toTileInv; - instMat = - translate(instMat, glm::dvec3(decodedInstances.positions[i])); - if (hasRotations) { - instMat = - instMat * toMat4(glm::dquat(decodedInstances.rotations[i])); - } - if (hasScales) { - instMat = scale(instMat, glm::dvec3(decodedInstances.scales[i])); - } - instMat = instMat * toTile; - glm::dvec3 position, scale, skew; - glm::dquat rotation; - glm::dvec4 perspective; - decompose(instMat, scale, rotation, position, skew, perspective); - glm::vec3 fposition(position); - std::memcpy( - &instanceBuffer.cesium.data[i * totalStride], - &fposition, - sizeof(fposition)); - if (hasRotations) { - glm::quat frotation(rotation); - std::memcpy( - &instanceBuffer.cesium.data[i * totalStride + rotOffset], - &frotation, - sizeof(frotation)); + auto instMat = composeInstanceTransform(i, decodedInstances); + instMat = toTileInv * instMat * toTile; + for (const auto& innerMat : existingInstances) { + auto finalMat = instMat * innerMat; + glm::dvec3 position, scale, skew; + glm::dquat rotation; + glm::dvec4 perspective; + decompose(finalMat, scale, rotation, position, skew, perspective); + copyToBuffer( + position, + rotation, + scale, + instanceBuffer.cesium.data, + destInstanceIndx++); } - if (hasScales) { - glm::vec3 fscale(scale); - std::memcpy( - &instanceBuffer.cesium.data[i * totalStride + scaleOffset], - &fscale, - sizeof(fscale)); - } - } - auto& gpuExt = node.addExtension(); - if (!gpuExt.attributes.empty()) { - // wtf } auto posAccessorId = createAccessorInGltf( gltf, instanceBufferViewId, Accessor::ComponentType::FLOAT, - numInstances, + numNewInstances, Accessor::Type::VEC3); auto& posAcessor = gltf.accessors[static_cast(posAccessorId)]; posAcessor.byteOffset = dataBaseOffset; gpuExt.attributes["TRANSLATION"] = posAccessorId; - if (hasRotations) { - auto rotAccessorId = createAccessorInGltf( - gltf, - instanceBufferViewId, - Accessor::ComponentType::FLOAT, - numInstances, - Accessor::Type::VEC4); - auto& rotAccessor = - gltf.accessors[static_cast(rotAccessorId)]; - rotAccessor.byteOffset = - static_cast(dataBaseOffset + rotOffset); - gpuExt.attributes["ROTATION"] = rotAccessorId; - } - if (hasScales) { - auto scaleAccessorId = createAccessorInGltf( - gltf, - instanceBufferViewId, - Accessor::ComponentType::FLOAT, - numInstances, - Accessor::Type::VEC3); - auto& scaleAccessor = - gltf.accessors[static_cast(scaleAccessorId)]; - scaleAccessor.byteOffset = - static_cast(dataBaseOffset + scaleOffset); - gpuExt.attributes["SCALE"] = scaleAccessorId; - } + auto rotAccessorId = createAccessorInGltf( + gltf, + instanceBufferViewId, + Accessor::ComponentType::FLOAT, + numInstances, + Accessor::Type::VEC4); + auto& rotAccessor = + gltf.accessors[static_cast(rotAccessorId)]; + rotAccessor.byteOffset = + static_cast(dataBaseOffset + rotOffset); + gpuExt.attributes["ROTATION"] = rotAccessorId; + auto scaleAccessorId = createAccessorInGltf( + gltf, + instanceBufferViewId, + Accessor::ComponentType::FLOAT, + numInstances, + Accessor::Type::VEC3); + auto& scaleAccessor = + gltf.accessors[static_cast(scaleAccessorId)]; + scaleAccessor.byteOffset = + static_cast(dataBaseOffset + scaleOffset); + gpuExt.attributes["SCALE"] = scaleAccessorId; }); if (decodedInstances.rtcCenter) { applyRTC(*result.model, *decodedInstances.rtcCenter); From 46581092d18fb06807b4b7b8f74433d3ad9ebb59 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Fri, 26 Apr 2024 16:54:18 +0200 Subject: [PATCH 15/32] Refactor with helpers for converting glTF values to glm. --- .../Cesium3DTilesContent/LegacyUtilities.h | 29 +++++++++ .../src/I3dmToGltfConverter.cpp | 60 ++++--------------- .../include/CesiumGltf/AccessorUtility.h | 17 ++++++ CesiumGltf/src/AccessorUtility.cpp | 34 +++++++++++ 4 files changed, 90 insertions(+), 50 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h index c9a95d5f3..080ac9cd9 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include @@ -100,5 +102,32 @@ int32_t createAccessorInGltf( const std::string type); void applyRTC(CesiumGltf::Model& gltf, const glm::dvec3& rtc); + +template +GLMType toGlm(const GLTFType& gltfVal); + +template +GLMType toGlm(const CesiumGltf::AccessorTypes::VEC3& gltfVal) { + return GLMType(gltfVal.value[0], gltfVal.value[1], gltfVal.value[2]); +} + +template +GLMType +toGlmQuat(const CesiumGltf::AccessorTypes::VEC4& gltfVal) { + if constexpr (std::is_same()) { + return GLMType( + gltfVal.value[3], + gltfVal.value[0], + gltfVal.value[1], + gltfVal.value[2]); + } else { + return GLMType( + CesiumGltf::normalize(gltfVal.value[3]), + CesiumGltf::normalize(gltfVal.value[0]), + CesiumGltf::normalize(gltfVal.value[1]), + CesiumGltf::normalize(gltfVal.value[2])); + } +} + } // namespace LegacyUtilities } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp index 62e8a2e45..f16c82a99 100644 --- a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp @@ -475,26 +475,6 @@ composeInstanceTransform(size_t i, const DecodedInstances& decodedInstances) { return result; } -// Helpers for different instancing rotation formats - -template struct is_float_quat : std::false_type {}; - -template <> -struct is_float_quat> : std::true_type { -}; - -template struct is_int_quat : std::false_type {}; - -template -struct is_int_quat> - : std::conjunction, std::is_signed> {}; - -template -inline constexpr bool is_float_quat_v = is_float_quat::value; - -template -inline constexpr bool is_int_quat_v = is_int_quat::value; - std::vector meshGpuInstances( GltfConverterResult& result, const ExtensionExtMeshGpuInstancing& gpuExt) { @@ -552,10 +532,7 @@ std::vector meshGpuInstances( *translations); if (translationAccessor.status() == AccessorViewStatus::Valid) { for (unsigned i = 0; i < count; ++i) { - glm::dvec3 transVec( - translationAccessor[i].value[0], - translationAccessor[i].value[1], - translationAccessor[i].value[2]); + auto transVec = toGlm(translationAccessor[i]); instances[i] = glm::translate(instances[i], transVec); } } else { @@ -563,29 +540,15 @@ std::vector meshGpuInstances( } } if (rotations) { - createAccessorView(model, *rotations, [&](auto&& quatView) -> void { - using QuatType = decltype(quatView[0]); - using ValueType = std::decay_t; - if constexpr (is_float_quat_v) { - for (unsigned i = 0; i < count; ++i) { - glm::dquat quat( - quatView[i].value[3], - quatView[i].value[0], - quatView[i].value[1], - quatView[i].value[2]); - instances[i] = instances[i] * glm::toMat4(quat); - } - } else if constexpr (is_int_quat_v) { - for (unsigned i = 0; i < count; ++i) { - float val[4]; - for (unsigned j = 0; j < 4; ++j) { - val[j] = static_cast(normalize(quatView[i].value[j])); + auto quatAccessorView = getQuaternionAccessorView(model, rotations); + std::visit( + [&](auto&& arg) { + for (unsigned i = 0; i < count; ++i) { + auto quat = toGlmQuat(arg[i]); + instances[i] = instances[i] * glm::toMat4(quat); } - glm::dquat quat(val[3], val[0], val[1], val[2]); - instances[i] = instances[i] * glm::toMat4(quat); - } - } - }); + }, + quatAccessorView); } if (scales) { AccessorView> scaleAccessor( @@ -593,10 +556,7 @@ std::vector meshGpuInstances( *scales); if (scaleAccessor.status() == AccessorViewStatus::Valid) { for (unsigned i = 0; i < count; ++i) { - glm::dvec3 scaleFactors( - scaleAccessor[i].value[0], - scaleAccessor[i].value[1], - scaleAccessor[i].value[2]); + auto scaleFactors = toGlm(scaleAccessor[i]); instances[i] = glm::scale(instances[i], scaleFactors); } } else { diff --git a/CesiumGltf/include/CesiumGltf/AccessorUtility.h b/CesiumGltf/include/CesiumGltf/AccessorUtility.h index b679186f3..6037469a7 100644 --- a/CesiumGltf/include/CesiumGltf/AccessorUtility.h +++ b/CesiumGltf/include/CesiumGltf/AccessorUtility.h @@ -285,4 +285,21 @@ struct TexCoordFromAccessor { int64_t index; }; +/** + * Type definition for quaternion accessors, as defined for ExtMeshGpuInstancing + * and animation samplers. + */ +typedef std::variant< + AccessorView>, + AccessorView>, + AccessorView>, + AccessorView>, + AccessorView>> + QuaternionAccessorType; + +QuaternionAccessorType +getQuaternionAccessorView(const Model& model, const Accessor* accessor); + +QuaternionAccessorType +getQuaternionAccessorView(const Model& model, int32_t accessorIndex); } // namespace CesiumGltf diff --git a/CesiumGltf/src/AccessorUtility.cpp b/CesiumGltf/src/AccessorUtility.cpp index 5a4383b21..35ae406c1 100644 --- a/CesiumGltf/src/AccessorUtility.cpp +++ b/CesiumGltf/src/AccessorUtility.cpp @@ -133,4 +133,38 @@ TexCoordAccessorType getTexCoordAccessorView( } } +QuaternionAccessorType +getQuaternionAccessorView(const Model& model, const Accessor* pAccessor) { + if (!pAccessor) { + return QuaternionAccessorType(); + } + switch (pAccessor->componentType) { + case Accessor::ComponentType::BYTE: + return AccessorView>(model, *pAccessor); + [[fallthrough]]; + case Accessor::ComponentType::UNSIGNED_BYTE: + return AccessorView>(model, *pAccessor); + [[fallthrough]]; + case Accessor::ComponentType::SHORT: + return AccessorView>(model, *pAccessor); + [[fallthrough]]; + case Accessor::ComponentType::UNSIGNED_SHORT: + return AccessorView>(model, *pAccessor); + [[fallthrough]]; + case Accessor::ComponentType::FLOAT: + return AccessorView>(model, *pAccessor); + default: + return QuaternionAccessorType(); + } +} + +QuaternionAccessorType +getQuaternionAccessorView(const Model& model, int32_t accessorIndex) { + const Accessor* pAccessor = + model.getSafe(&model.accessors, accessorIndex); + if (!pAccessor || pAccessor->type != Accessor::Type::VEC4) { + return QuaternionAccessorType(); + } + return getQuaternionAccessorView(model, pAccessor); +} } // namespace CesiumGltf From 1cd4ab750cf3b6d2b5bc177c74c1207ace44cd66 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Tue, 7 May 2024 15:51:21 +0200 Subject: [PATCH 16/32] Modify Cesium3DTilesContent tests to use ConverterSubprocessor Tests compile again. --- .../test/ConvertTileToGltf.cpp | 41 ++++++++++ Cesium3DTilesContent/test/ConvertTileToGltf.h | 18 +++-- ...gradeBatchTableToExtStructuralMetadata.cpp | 2 +- .../include/CesiumNativeTests/FileAccessor.h | 25 ++++++ CesiumNativeTests/src/FileAccessor.cpp | 79 +++++++++++++++++++ package-lock.json | 4 +- 6 files changed, 160 insertions(+), 9 deletions(-) create mode 100644 Cesium3DTilesContent/test/ConvertTileToGltf.cpp create mode 100644 CesiumNativeTests/include/CesiumNativeTests/FileAccessor.h create mode 100644 CesiumNativeTests/src/FileAccessor.cpp diff --git a/Cesium3DTilesContent/test/ConvertTileToGltf.cpp b/Cesium3DTilesContent/test/ConvertTileToGltf.cpp new file mode 100644 index 000000000..0d6a5d25a --- /dev/null +++ b/Cesium3DTilesContent/test/ConvertTileToGltf.cpp @@ -0,0 +1,41 @@ +#include "ConvertTileToGltf.h" + +#include +#include +#include + +namespace Cesium3DTilesContent { + +CesiumAsync::AsyncSystem ConvertTileToGltf::asyncSystem( + std::make_shared()); + +ConverterSubprocessor +ConvertTileToGltf::makeSubprocessor(const std::string& baseUrl) { + auto fileAccessor = std::make_shared(); + std::vector requestHeaders; + return ConverterSubprocessor( + asyncSystem, + fileAccessor, + baseUrl, + glm::dmat4(1.0), + requestHeaders); +} + +GltfConverterResult ConvertTileToGltf::fromB3dm( + const std::filesystem::path& filePath, + const CesiumGltfReader::GltfReaderOptions& options) { + ConverterSubprocessor subprocessor = makeSubprocessor(""); + auto bytes = readFile(filePath); + auto future = B3dmToGltfConverter::convert(bytes, options, subprocessor); + return future.wait(); +} + +GltfConverterResult ConvertTileToGltf::fromPnts( + const std::filesystem::path& filePath, + const CesiumGltfReader::GltfReaderOptions& options) { + ConverterSubprocessor subprocessor = makeSubprocessor(""); + auto bytes = readFile(filePath); + auto future = PntsToGltfConverter::convert(bytes, options, subprocessor); + return future.wait(); +} +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/test/ConvertTileToGltf.h b/Cesium3DTilesContent/test/ConvertTileToGltf.h index cbffcf9e3..7517f04c5 100644 --- a/Cesium3DTilesContent/test/ConvertTileToGltf.h +++ b/Cesium3DTilesContent/test/ConvertTileToGltf.h @@ -1,7 +1,10 @@ #pragma once #include +#include #include +#include +#include #include #include @@ -10,12 +13,15 @@ namespace Cesium3DTilesContent { class ConvertTileToGltf { public: - static GltfConverterResult fromB3dm(const std::filesystem::path& filePath) { - return B3dmToGltfConverter::convert(readFile(filePath), {}, nullptr); - } + static GltfConverterResult fromB3dm( + const std::filesystem::path& filePath, + const CesiumGltfReader::GltfReaderOptions& options = {}); + static GltfConverterResult fromPnts( + const std::filesystem::path& filePath, + const CesiumGltfReader::GltfReaderOptions& options = {}); - static GltfConverterResult fromPnts(const std::filesystem::path& filePath) { - return PntsToGltfConverter::convert(readFile(filePath), {}, nullptr); - } +private: + static CesiumAsync::AsyncSystem asyncSystem; + static ConverterSubprocessor makeSubprocessor(const std::string& baseUrl); }; } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp b/Cesium3DTilesContent/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp index b419df9d9..87781484f 100644 --- a/Cesium3DTilesContent/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp +++ b/Cesium3DTilesContent/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp @@ -1065,7 +1065,7 @@ TEST_CASE("Draco-compressed b3dm uses _FEATURE_ID_0 attribute name in glTF") { options.decodeDraco = false; GltfConverterResult result = - B3dmToGltfConverter::convert(readFile(testFilePath), options); + ConvertTileToGltf::fromB3dm(testFilePath, options); CHECK(result.errors.errors.empty()); CHECK(result.errors.warnings.empty()); REQUIRE(result.model); diff --git a/CesiumNativeTests/include/CesiumNativeTests/FileAccessor.h b/CesiumNativeTests/include/CesiumNativeTests/FileAccessor.h new file mode 100644 index 000000000..5b920ef14 --- /dev/null +++ b/CesiumNativeTests/include/CesiumNativeTests/FileAccessor.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +#include + +namespace CesiumNativeTests { +class FileAccessor : public CesiumAsync::IAssetAccessor { +public: + CesiumAsync::Future> + get(const CesiumAsync::AsyncSystem& asyncSystem, + const std::string& url, + const std::vector&) override; + + CesiumAsync::Future> request( + const CesiumAsync::AsyncSystem& asyncSystem, + const std::string& /* verb */, + const std::string& url, + const std::vector& headers, + const gsl::span&) override; + + void tick() noexcept override {} +}; +} // namespace CesiumNativeTests diff --git a/CesiumNativeTests/src/FileAccessor.cpp b/CesiumNativeTests/src/FileAccessor.cpp new file mode 100644 index 000000000..99b3bf691 --- /dev/null +++ b/CesiumNativeTests/src/FileAccessor.cpp @@ -0,0 +1,79 @@ +#include +#include + +#include +#include + +namespace CesiumNativeTests { + +namespace { +std::unique_ptr readFileUri(const std::string& uri) { + + std::vector result; + CesiumAsync::HttpHeaders headers; + std::string contentType; + auto response = [&](uint64_t errorCode) { + return std::make_unique( + errorCode, + contentType, + headers, + std::move(result)); + }; + auto protocolPos = uri.find("file:///"); + if (protocolPos != 0) { + return response(400); + } + std::string path = uri.substr(std::strlen("file://")); + std::ifstream file(path, std::ios::binary | std::ios::ate); + if (!file) { + return response(404); + } + std::streamsize size = file.tellg(); + file.seekg(0, std::ios::beg); + result.resize(static_cast(size)); + file.read(reinterpret_cast(result.data()), size); + if (!file) { + return response(503); + } else { + contentType = "application/octet-stream"; + headers.insert({"content-type", contentType}); + return response(200); + } +} +} // namespace + +CesiumAsync::Future> +FileAccessor::get( + const CesiumAsync::AsyncSystem& asyncSystem, + const std::string& url, + const std::vector& headers) { + return asyncSystem.createFuture>( + [&](const auto& promise) { + auto response = readFileUri(url); + CesiumAsync::HttpHeaders cesiumHeaders(headers.begin(), headers.end()); + auto request = std::make_shared( + "GET", + url, + cesiumHeaders, + std::move(response)); + promise.resolve(request); + }); +} + +// Can we do anything with a request that isn't a GET? +CesiumAsync::Future> +FileAccessor::request( + const CesiumAsync::AsyncSystem& asyncSystem, + const std::string& verb, + const std::string& url, + const std::vector& headers, + const gsl::span&) { + if (verb == "GET") { + return get(asyncSystem, url, headers); + } + return asyncSystem.createFuture>( + [&](const auto& promise) { + promise.reject(std::runtime_error("unsupported operation")); + }); +} +} // namespace CesiumNativeTests diff --git a/package-lock.json b/package-lock.json index ed16c2a54..d0980523a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "cesium-native", - "version": "0.33.0", + "version": "0.35.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cesium-native", - "version": "0.33.0", + "version": "0.35.0", "license": "Apache-2.0", "devDependencies": { "clang-format": "^1.5.0" From 5731d48998cacb1370af45105c1b4a00ccf252f0 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Tue, 7 May 2024 16:27:42 +0200 Subject: [PATCH 17/32] Squash some CI compilation errors Fix a real truncation issue, and some noise from overzealous gcc. --- Cesium3DTilesContent/src/I3dmToGltfConverter.cpp | 16 ++++++++-------- Cesium3DTilesContent/src/PntsToGltfConverter.cpp | 14 +++++++------- CesiumNativeTests/src/FileAccessor.cpp | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp index f16c82a99..2f4ee4db5 100644 --- a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp @@ -28,14 +28,14 @@ using namespace LegacyUtilities; namespace { struct InstancesHeader { - unsigned char magic[4]; - uint32_t version; - uint32_t byteLength; - uint32_t featureTableJsonByteLength; - uint32_t featureTableBinaryByteLength; - uint32_t batchTableJsonByteLength; - uint32_t batchTableBinaryByteLength; - uint32_t gltfFormat; + unsigned char magic[4] = {0, 0, 0, 0}; + uint32_t version = 0; + uint32_t byteLength = 0; + uint32_t featureTableJsonByteLength = 0; + uint32_t featureTableBinaryByteLength = 0; + uint32_t batchTableJsonByteLength = 0; + uint32_t batchTableBinaryByteLength = 0; + uint32_t gltfFormat = 0; }; struct DecodedInstances { diff --git a/Cesium3DTilesContent/src/PntsToGltfConverter.cpp b/Cesium3DTilesContent/src/PntsToGltfConverter.cpp index f9a1ce55a..9f3c22b0a 100644 --- a/Cesium3DTilesContent/src/PntsToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/PntsToGltfConverter.cpp @@ -33,13 +33,13 @@ using namespace CesiumUtility; namespace Cesium3DTilesContent { namespace { struct PntsHeader { - unsigned char magic[4]; - uint32_t version; - uint32_t byteLength; - uint32_t featureTableJsonByteLength; - uint32_t featureTableBinaryByteLength; - uint32_t batchTableJsonByteLength; - uint32_t batchTableBinaryByteLength; + unsigned char magic[4] = {0, 0, 0, 0}; + uint32_t version = 0; + uint32_t byteLength = 0; + uint32_t featureTableJsonByteLength = 0; + uint32_t featureTableBinaryByteLength = 0; + uint32_t batchTableJsonByteLength = 0; + uint32_t batchTableBinaryByteLength = 0; }; void parsePntsHeader( diff --git a/CesiumNativeTests/src/FileAccessor.cpp b/CesiumNativeTests/src/FileAccessor.cpp index 99b3bf691..cc94e8716 100644 --- a/CesiumNativeTests/src/FileAccessor.cpp +++ b/CesiumNativeTests/src/FileAccessor.cpp @@ -12,7 +12,7 @@ std::unique_ptr readFileUri(const std::string& uri) { std::vector result; CesiumAsync::HttpHeaders headers; std::string contentType; - auto response = [&](uint64_t errorCode) { + auto response = [&](uint16_t errorCode) { return std::make_unique( errorCode, contentType, From d2380256211db401d74f0350a62d85786e02d2ed Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Tue, 7 May 2024 17:58:06 +0200 Subject: [PATCH 18/32] Add a perpVec function and use it A simple function for computing a perpendicular vector. --- .../src/I3dmToGltfConverter.cpp | 21 +++++-------------- CesiumUtility/include/CesiumUtility/Math.h | 16 ++++++++++++++ CesiumUtility/test/TestMath.cpp | 13 ++++++++++++ 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp index 2f4ee4db5..57127a89f 100644 --- a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -162,23 +163,12 @@ glm::quat rotation(const glm::vec3& vec1, const glm::vec3& vec2) { return glm::quat(1.0, 0.0f, 0.0f, 0.0f); } if (cosRot <= -1.0f) { - // Choose principal axis that is "most orthogonal" to the first vector. - glm::vec3 orthoVec(0.0f, 0.0f, 0.0f); - glm::vec3 absVals(std::fabs(vec1.x), std::fabs(vec1.y), std::fabs(vec1.z)); - if (absVals.z <= absVals.x && absVals.z <= absVals.y) { - orthoVec.z = 1.0f; - } else if (absVals.x <= absVals.y) { - orthoVec.x = 1.0f; - } else { - orthoVec.y = 1.0f; - } - auto rotAxis = cross(vec1, orthoVec); - rotAxis = normalize(rotAxis); + auto rotAxis = CesiumUtility::Math::perpVec(vec1); // rotation by pi radians - return glm::quat(0.0f, rotAxis.x, rotAxis.y, rotAxis.z); + return glm::quat(0.0f, rotAxis); } auto rotAxis = cross(vec1, vec2); - glm::quat sumQuat(cosRot + 1.0f, rotAxis.x, rotAxis.y, rotAxis.z); + glm::quat sumQuat(cosRot + 1.0f, rotAxis); return normalize(sumQuat); } @@ -188,8 +178,7 @@ glm::quat rotationFromUpRight(const glm::vec3& up, const glm::vec3& right) { // We can rotate a point vector by a quaternion using q * (0, v) * // conj(q). But here we are doing an inverse rotation of the right vector into // the "up frame." - glm::quat temp = - conjugate(upRot) * glm::quat(0.0f, right.x, right.y, right.z) * upRot; + glm::quat temp = conjugate(upRot) * glm::quat(0.0f, right) * upRot; glm::vec3 innerRight(temp.x, temp.y, temp.z); glm::quat rightRot = rotation(glm::vec3(1.0f, 0.0f, 0.0f), innerRight); return upRot * rightRot; diff --git a/CesiumUtility/include/CesiumUtility/Math.h b/CesiumUtility/include/CesiumUtility/Math.h index b7a021fb9..ff055ac21 100644 --- a/CesiumUtility/include/CesiumUtility/Math.h +++ b/CesiumUtility/include/CesiumUtility/Math.h @@ -451,6 +451,22 @@ class CESIUMUTILITY_API Math final { return down; } } + + /** + * @brief Construct a vector perpendicular to the argument. + * @param v input vector + * @return a perpendicular vector + */ + template + static glm::vec<3, T, Q> perpVec(const glm::vec<3, T, Q>& v) { + // This constructs a vector whose dot product with v will be 0, hence + // perpendicular to v. As seen in the "Physically Based Rendering". + if (std::abs(v.x) > std::abs(v.y)) { + return glm::vec<3, T, Q>(-v.z, 0, v.x) / std::sqrt(v.x * v.x + v.z * v.z); + } + return glm::vec<3, T, Q>( 0, v.z, -v.y) / std::sqrt(v.y * v.y + v.z * v.z); + } + }; } // namespace CesiumUtility diff --git a/CesiumUtility/test/TestMath.cpp b/CesiumUtility/test/TestMath.cpp index 1eb21aef2..dc55f03a2 100644 --- a/CesiumUtility/test/TestMath.cpp +++ b/CesiumUtility/test/TestMath.cpp @@ -1,6 +1,7 @@ #include "CesiumUtility/Math.h" #include +#include using namespace CesiumUtility; @@ -144,3 +145,15 @@ TEST_CASE("Math::mod") { CHECK(Math::mod(-1.0, -1.0) == -0.0); CHECK(Math::equalsEpsilon(Math::mod(-1.1, -1.0), -0.1, Math::Epsilon15)); } + +TEST_CASE("Math::perpVec") { + glm::vec3 v0(.2f, .3f, .4f); + glm::vec3 perp = Math::perpVec(v0); + // perp is normalized + glm::vec3 mutual = glm::cross(v0, perp); + CHECK(Math::equalsEpsilon(length(v0), length(mutual), Math::Epsilon5)); + glm::vec3 v1(.3f, .2f, -1.0f); + glm::vec3 perp1 = Math::perpVec(v1); + glm::vec3 mutual1 = glm::cross(v1, perp1); + CHECK(Math::equalsEpsilon(length(v1), length(mutual1), Math::Epsilon5)); +} From ea79bb8a762f52576788a2ff1a8bf13832739458 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Thu, 16 May 2024 17:05:33 +0200 Subject: [PATCH 19/32] Reponse to feedback: change ConverterSubprocessor to AssetFetcher --- .../B3dmToGltfConverter.h | 4 +- .../BinaryToGltfConverter.h | 6 +-- .../CmptToGltfConverter.h | 4 +- .../Cesium3DTilesContent/GltfConverters.h | 24 ++++++--- .../I3dmToGltfConverter.h | 4 +- .../Cesium3DTilesContent/LegacyUtilities.h | 8 --- .../PntsToGltfConverter.h | 4 +- .../src/B3dmToGltfConverter.cpp | 12 ++--- .../src/BinaryToGltfConverter.cpp | 8 +-- .../src/CmptToGltfConverter.cpp | 16 +++--- Cesium3DTilesContent/src/GltfConverters.cpp | 50 ++++++++++++++++--- .../src/I3dmToGltfConverter.cpp | 22 ++++---- Cesium3DTilesContent/src/LegacyUtilities.cpp | 37 -------------- .../src/PntsToGltfConverter.cpp | 4 +- .../test/ConvertTileToGltf.cpp | 14 +++--- .../src/ImplicitOctreeLoader.cpp | 4 +- .../src/ImplicitQuadtreeLoader.cpp | 4 +- .../src/TilesetJsonLoader.cpp | 4 +- .../include/CesiumGltf/AccessorUtility.h | 4 +- CesiumGltf/include/CesiumGltf/Model.h | 3 +- 20 files changed, 117 insertions(+), 119 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/B3dmToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/B3dmToGltfConverter.h index 69be1ec1f..c21c6368e 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/B3dmToGltfConverter.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/B3dmToGltfConverter.h @@ -11,12 +11,12 @@ #include namespace Cesium3DTilesContent { -struct ConverterSubprocessor; +struct AssetFetcher; struct B3dmToGltfConverter { static CesiumAsync::Future convert( const gsl::span& b3dmBinary, const CesiumGltfReader::GltfReaderOptions& options, - const ConverterSubprocessor& subprocessor); + const AssetFetcher& assetFetcher); }; } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h index 08ad61350..6e95e98e2 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h @@ -9,20 +9,20 @@ #include namespace Cesium3DTilesContent { -struct ConverterSubprocessor; +struct AssetFetcher; struct BinaryToGltfConverter { public: static CesiumAsync::Future convert( const gsl::span& gltfBinary, const CesiumGltfReader::GltfReaderOptions& options, - const ConverterSubprocessor& subProcessor); + const AssetFetcher& assetFetcher); private: static GltfConverterResult convertImmediate( const gsl::span& gltfBinary, const CesiumGltfReader::GltfReaderOptions& options, - const ConverterSubprocessor& subProcessor); + const AssetFetcher& assetFetcher); static CesiumGltfReader::GltfReader _gltfReader; }; } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/CmptToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/CmptToGltfConverter.h index 7c39f35f8..836d103be 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/CmptToGltfConverter.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/CmptToGltfConverter.h @@ -9,12 +9,12 @@ #include namespace Cesium3DTilesContent { -struct ConverterSubprocessor; +struct AssetFetcher; struct CmptToGltfConverter { static CesiumAsync::Future convert( const gsl::span& cmptBinary, const CesiumGltfReader::GltfReaderOptions& options, - const ConverterSubprocessor&); + const AssetFetcher& assetFetcher); }; } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h index 802349468..3c2047d8d 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h @@ -15,12 +15,17 @@ namespace Cesium3DTilesContent { +struct ByteResult { + std::vector 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& pAssetAccessor_, const std::string& baseUrl_, @@ -31,10 +36,13 @@ struct CESIUM3DTILESCONTENT_API ConverterSubprocessor { baseUrl(baseUrl_), tileTransform(tileTransform_), requestHeaders(requestHeaders_) {} + + CesiumAsync::Future get(const std::string& relativeUrl) const; + const CesiumAsync::AsyncSystem& asyncSystem; const std::shared_ptr pAssetAccessor; const std::string baseUrl; - glm::dmat4 tileTransform; + glm::dmat4 tileTransform; // For ENU transforms in i3dm const std::vector& requestHeaders; }; @@ -61,7 +69,7 @@ class CESIUM3DTILESCONTENT_API GltfConverters { using ConverterFunction = CesiumAsync::Future (*)( const gsl::span& content, const CesiumGltfReader::GltfReaderOptions& options, - const ConverterSubprocessor& subprocessor); + const AssetFetcher& subprocessor); /** * @brief Register the given function for the given magic header. @@ -151,7 +159,7 @@ class CESIUM3DTILESCONTENT_API GltfConverters { const std::string& filePath, const gsl::span& content, const CesiumGltfReader::GltfReaderOptions& options, - const ConverterSubprocessor& subprocessor); + const AssetFetcher& subprocessor); /** * @brief Creates the {@link GltfConverterResult} from the given @@ -176,7 +184,7 @@ class CESIUM3DTILESCONTENT_API GltfConverters { static CesiumAsync::Future convert( const gsl::span& content, const CesiumGltfReader::GltfReaderOptions& options, - const ConverterSubprocessor& subprocessor); + const AssetFetcher& subprocessor); private: static std::string toLowerCase(const std::string_view& str); diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/I3dmToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/I3dmToGltfConverter.h index 698e6144a..f709903ac 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/I3dmToGltfConverter.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/I3dmToGltfConverter.h @@ -11,12 +11,12 @@ #include namespace Cesium3DTilesContent { -struct ConverterSubprocessor; +struct AssetFetcher; struct I3dmToGltfConverter { static CesiumAsync::Future convert( const gsl::span& instancesBinary, const CesiumGltfReader::GltfReaderOptions& options, - const ConverterSubprocessor& subprocessor); + const AssetFetcher& assetFetcher); }; } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h index 080ac9cd9..84b93b6c8 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h @@ -21,14 +21,6 @@ class Buffer; namespace Cesium3DTilesContent { -struct ByteResult { - std::vector bytes; - CesiumUtility::ErrorList errorList; -}; - -CesiumAsync::Future -get(const ConverterSubprocessor& subprocessor, const std::string& relativeUrl); - namespace LegacyUtilities { std::optional parseOffset( const rapidjson::Document& document, diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/PntsToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/PntsToGltfConverter.h index 4215194f1..31a61a200 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/PntsToGltfConverter.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/PntsToGltfConverter.h @@ -11,12 +11,12 @@ #include namespace Cesium3DTilesContent { -struct ConverterSubprocessor; +struct AssetFetcher; struct PntsToGltfConverter { static CesiumAsync::Future convert( const gsl::span& pntsBinary, const CesiumGltfReader::GltfReaderOptions& options, - const ConverterSubprocessor& subprocessor); + const AssetFetcher& assetFetcher); }; } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/B3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/B3dmToGltfConverter.cpp index 04a49a6e2..0f2c423c9 100644 --- a/Cesium3DTilesContent/src/B3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/B3dmToGltfConverter.cpp @@ -119,7 +119,7 @@ CesiumAsync::Future 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 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 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 B3dmToGltfConverter::convert( const gsl::span& 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 B3dmToGltfConverter::convert( header, headerLength, options, - subprocessor) + assetFetcher) .thenImmediately( [b3dmBinary, header, headerLength](GltfConverterResult&& glbResult) { if (!glbResult.errors) { diff --git a/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp b/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp index a6cb23f1a..942530787 100644 --- a/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp @@ -7,7 +7,7 @@ CesiumGltfReader::GltfReader BinaryToGltfConverter::_gltfReader; GltfConverterResult BinaryToGltfConverter::convertImmediate( const gsl::span& gltfBinary, const CesiumGltfReader::GltfReaderOptions& options, - const ConverterSubprocessor&) { + const AssetFetcher&) { CesiumGltfReader::GltfReaderResult loadedGltf = _gltfReader.readGltf(gltfBinary, options); @@ -21,8 +21,8 @@ GltfConverterResult BinaryToGltfConverter::convertImmediate( CesiumAsync::Future BinaryToGltfConverter::convert( const gsl::span& 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, assetFetcher)); } } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/CmptToGltfConverter.cpp b/Cesium3DTilesContent/src/CmptToGltfConverter.cpp index f7e7f076c..ae14d900d 100644 --- a/Cesium3DTilesContent/src/CmptToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/CmptToGltfConverter.cpp @@ -25,11 +25,11 @@ static_assert(sizeof(InnerHeader) == 12); CesiumAsync::Future CmptToGltfConverter::convert( const gsl::span& 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 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 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> innerTiles; @@ -80,7 +80,7 @@ CesiumAsync::Future 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 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&& innerResults) { if (innerResults.size() == 1) { return innerResults[0]; diff --git a/Cesium3DTilesContent/src/GltfConverters.cpp b/Cesium3DTilesContent/src/GltfConverters.cpp index c01bb71c0..caa8b263c 100644 --- a/Cesium3DTilesContent/src/GltfConverters.cpp +++ b/Cesium3DTilesContent/src/GltfConverters.cpp @@ -1,4 +1,6 @@ +#include #include +#include #include @@ -41,17 +43,17 @@ CesiumAsync::Future GltfConverters::convert( const std::string& filePath, const gsl::span& 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 GltfConverters::convert( fileExtension, magic)); - return subprocessor.asyncSystem.createResolvedFuture( + return assetFetcher.asyncSystem.createResolvedFuture( GltfConverterResult{std::nullopt, std::move(errors)}); } CesiumAsync::Future GltfConverters::convert( const gsl::span& 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 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 +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&& 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 asset = pResponse->data(); + std::copy( + asset.begin(), + asset.end(), + std::back_inserter(byteResult.bytes)); + return asyncSystem.createResolvedFuture(std::move(byteResult)); + }); +} } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp index 2f4ee4db5..a35677811 100644 --- a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp @@ -220,13 +220,13 @@ CesiumAsync::Future convertInstancesContent( const InstancesHeader& header, uint32_t headerLength, const CesiumGltfReader::GltfReaderOptions& options, - const ConverterSubprocessor& subprocessor, + const AssetFetcher& assetFetcher, GltfConverterResult& result) { ConvertResult subResult; DecodedInstances& decodedInstances = subResult.decodedInstances; subResult.gltfResult = result; auto finishEarly = [&]() { - return subprocessor.asyncSystem.createResolvedFuture(std::move(subResult)); + return assetFetcher.asyncSystem.createResolvedFuture(std::move(subResult)); }; if (header.featureTableJsonByteLength == 0 || header.featureTableBinaryByteLength == 0) { @@ -381,7 +381,7 @@ CesiumAsync::Future convertInstancesContent( decodedInstances.rotations[i] = rotationFromUpRight(dUp, dRight); } } else if (decodedInstances.rotationENU) { - glm::dmat4 worldTransform = subprocessor.tileTransform; + glm::dmat4 worldTransform = assetFetcher.tileTransform; if (decodedInstances.rtcCenter) { worldTransform = translate(worldTransform, *decodedInstances.rtcCenter); } @@ -423,20 +423,20 @@ CesiumAsync::Future convertInstancesContent( auto gltfUri = std::string( reinterpret_cast(gltfData.data()), gltfData.size()); - return get(subprocessor, gltfUri) + return assetFetcher.get(gltfUri) .thenImmediately( - [options, subprocessor](ByteResult&& byteResult) + [options, assetFetcher](ByteResult&& byteResult) -> CesiumAsync::Future { 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)]( GltfConverterResult&& converterResult) mutable { @@ -448,7 +448,7 @@ CesiumAsync::Future convertInstancesContent( return subResult; }); } else { - return BinaryToGltfConverter::convert(gltfData, options, subprocessor) + return BinaryToGltfConverter::convert(gltfData, options, assetFetcher) .thenImmediately([subResult = std::move(subResult)]( GltfConverterResult&& converterResult) mutable { if (converterResult.errors.hasErrors()) { @@ -709,13 +709,13 @@ void instantiateInstances( CesiumAsync::Future I3dmToGltfConverter::convert( const gsl::span& instancesBinary, const CesiumGltfReader::GltfReaderOptions& options, - const ConverterSubprocessor& subProcessor) { + const AssetFetcher& assetFetcher) { GltfConverterResult result; InstancesHeader header; uint32_t headerLength = 0; parseInstancesHeader(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( @@ -723,7 +723,7 @@ CesiumAsync::Future I3dmToGltfConverter::convert( header, headerLength, options, - subProcessor, + assetFetcher, result) .thenImmediately([](ConvertResult&& convertResult) { if (convertResult.gltfResult.errors.hasErrors()) { diff --git a/Cesium3DTilesContent/src/LegacyUtilities.cpp b/Cesium3DTilesContent/src/LegacyUtilities.cpp index 50bf3703c..4b2956133 100644 --- a/Cesium3DTilesContent/src/LegacyUtilities.cpp +++ b/Cesium3DTilesContent/src/LegacyUtilities.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -142,40 +141,4 @@ void applyRTC(Model& gltf, const glm::dvec3& rtc) { } } // namespace LegacyUtilities - -CesiumAsync::Future -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&& 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 asset = pResponse->data(); - std::copy( - asset.begin(), - asset.end(), - std::back_inserter(byteResult.bytes)); - return asyncSystem.createResolvedFuture(std::move(byteResult)); - }); -} } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/PntsToGltfConverter.cpp b/Cesium3DTilesContent/src/PntsToGltfConverter.cpp index 9f3c22b0a..e09996e15 100644 --- a/Cesium3DTilesContent/src/PntsToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/PntsToGltfConverter.cpp @@ -1633,7 +1633,7 @@ void convertPntsContentToGltf( CesiumAsync::Future PntsToGltfConverter::convert( const gsl::span& 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 PntsToGltfConverter::convert( convertPntsContentToGltf(pntsBinary, header, headerLength, result); } - return subprocessor.asyncSystem.createResolvedFuture(std::move(result)); + return assetFetcher.asyncSystem.createResolvedFuture(std::move(result)); } } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/test/ConvertTileToGltf.cpp b/Cesium3DTilesContent/test/ConvertTileToGltf.cpp index 0d6a5d25a..159a7c378 100644 --- a/Cesium3DTilesContent/test/ConvertTileToGltf.cpp +++ b/Cesium3DTilesContent/test/ConvertTileToGltf.cpp @@ -9,11 +9,11 @@ namespace Cesium3DTilesContent { CesiumAsync::AsyncSystem ConvertTileToGltf::asyncSystem( std::make_shared()); -ConverterSubprocessor -ConvertTileToGltf::makeSubprocessor(const std::string& baseUrl) { +AssetFetcher +ConvertTileToGltf::makeAssetFetcher(const std::string& baseUrl) { auto fileAccessor = std::make_shared(); std::vector 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 diff --git a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp index b22dd49b6..04954b271 100644 --- a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp @@ -152,13 +152,13 @@ CesiumAsync::Future 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 diff --git a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp index 84455218a..d2833425a 100644 --- a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp @@ -164,13 +164,13 @@ CesiumAsync::Future 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 diff --git a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp index 3f5266312..f9b021e8f 100644 --- a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp +++ b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp @@ -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); diff --git a/CesiumGltf/include/CesiumGltf/AccessorUtility.h b/CesiumGltf/include/CesiumGltf/AccessorUtility.h index 4a8f40e96..438bd01a3 100644 --- a/CesiumGltf/include/CesiumGltf/AccessorUtility.h +++ b/CesiumGltf/include/CesiumGltf/AccessorUtility.h @@ -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>, diff --git a/CesiumGltf/include/CesiumGltf/Model.h b/CesiumGltf/include/CesiumGltf/Model.h index a4b53bce0..b9e2e3f3a 100644 --- a/CesiumGltf/include/CesiumGltf/Model.h +++ b/CesiumGltf/include/CesiumGltf/Model.h @@ -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. */ From 068f70ed5043ec7e93bb3b1bd8ac278d1e02861a Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Fri, 17 May 2024 12:24:31 +0200 Subject: [PATCH 20/32] WIP responding to review comments --- .../BinaryToGltfConverter.h | 3 +- .../Cesium3DTilesContent/GltfConverters.h | 12 +- .../Cesium3DTilesContent/LegacyUtilities.h | 28 ++-- .../src/BinaryToGltfConverter.cpp | 5 +- .../src/I3dmToGltfConverter.cpp | 150 +++++++++--------- Cesium3DTilesContent/src/LegacyUtilities.cpp | 13 +- .../include/CesiumNativeTests/FileAccessor.h | 2 +- 7 files changed, 106 insertions(+), 107 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h index 6e95e98e2..ed4459de4 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h @@ -21,8 +21,7 @@ struct BinaryToGltfConverter { private: static GltfConverterResult convertImmediate( const gsl::span& gltfBinary, - const CesiumGltfReader::GltfReaderOptions& options, - const AssetFetcher& assetFetcher); + const CesiumGltfReader::GltfReaderOptions& options); static CesiumGltfReader::GltfReader _gltfReader; }; } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h index 3c2047d8d..decef5ff6 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h @@ -152,14 +152,16 @@ class CESIUM3DTILESCONTENT_API GltfConverters { * 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 convert( const std::string& filePath, const gsl::span& content, const CesiumGltfReader::GltfReaderOptions& options, - const AssetFetcher& subprocessor); + const AssetFetcher& assetFetcher); /** * @brief Creates the {@link GltfConverterResult} from the given @@ -178,13 +180,15 @@ class CESIUM3DTILESCONTENT_API GltfConverters { * * @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 convert( const gsl::span& content, const CesiumGltfReader::GltfReaderOptions& options, - const AssetFetcher& subprocessor); + const AssetFetcher& assetFetcher); private: static std::string toLowerCase(const std::string_view& str); diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h index 84b93b6c8..340fde579 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h @@ -22,7 +22,7 @@ class Buffer; namespace Cesium3DTilesContent { namespace LegacyUtilities { -std::optional parseOffset( +std::optional parseOffsetForSemantic( const rapidjson::Document& document, const char* semantic, CesiumUtility::ErrorList& errorList); @@ -76,9 +76,7 @@ parseArrayValueDVec3(const rapidjson::Value& arrayValue); std::optional parseArrayValueDVec3(const rapidjson::Document& document, const char* name); -int32_t createBufferInGltf(CesiumGltf::Model& gltf); -int32_t -createBufferInGltf(CesiumGltf::Model& gltf, std::vector&& buffer); +int32_t createBufferInGltf(CesiumGltf::Model& gltf, std::vector buffer = {}); int32_t createBufferViewInGltf( CesiumGltf::Model& gltf, @@ -95,25 +93,25 @@ int32_t createAccessorInGltf( void applyRTC(CesiumGltf::Model& gltf, const glm::dvec3& rtc); -template -GLMType toGlm(const GLTFType& gltfVal); +template +GlmType toGlm(const GLTFType& gltfVal); -template -GLMType toGlm(const CesiumGltf::AccessorTypes::VEC3& gltfVal) { - return GLMType(gltfVal.value[0], gltfVal.value[1], gltfVal.value[2]); +template +GlmType toGlm(const CesiumGltf::AccessorTypes::VEC3& gltfVal) { + return GlmType(gltfVal.value[0], gltfVal.value[1], gltfVal.value[2]); } -template -GLMType -toGlmQuat(const CesiumGltf::AccessorTypes::VEC4& gltfVal) { - if constexpr (std::is_same()) { - return GLMType( +template +GlmType +toGlmQuat(const CesiumGltf::AccessorTypes::VEC4& gltfVal) { + if constexpr (std::is_same()) { + 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]), diff --git a/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp b/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp index 942530787..9a9bcf401 100644 --- a/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp @@ -6,8 +6,7 @@ CesiumGltfReader::GltfReader BinaryToGltfConverter::_gltfReader; GltfConverterResult BinaryToGltfConverter::convertImmediate( const gsl::span& gltfBinary, - const CesiumGltfReader::GltfReaderOptions& options, - const AssetFetcher&) { + const CesiumGltfReader::GltfReaderOptions& options) { CesiumGltfReader::GltfReaderResult loadedGltf = _gltfReader.readGltf(gltfBinary, options); @@ -23,6 +22,6 @@ CesiumAsync::Future BinaryToGltfConverter::convert( const CesiumGltfReader::GltfReaderOptions& options, const AssetFetcher& assetFetcher) { return assetFetcher.asyncSystem.createResolvedFuture( - convertImmediate(gltfBinary, options, assetFetcher)); + convertImmediate(gltfBinary, options)); } } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp index a35677811..b668a73c9 100644 --- a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp @@ -27,7 +27,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; @@ -49,10 +49,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; } @@ -74,22 +77,22 @@ void rebaseInstances(DecodedInstances& decodedInstances) { decodedInstances.rtcCenter = newCenter; } -void parseInstancesHeader( +void parseI3dmHeader( const gsl::span& 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(instancesBinary.data()); + const I3dmHeader* pHeader = + reinterpret_cast(instancesBinary.data()); header = *pHeader; - headerLength = sizeof(InstancesHeader); + headerLength = sizeof(I3dmHeader); if (pHeader->version != 1) { result.errors.emplaceError(fmt::format( @@ -106,7 +109,7 @@ void parseInstancesHeader( } } -struct InstanceContent { +struct I3dmContent { uint32_t instancesLength = 0; std::optional rtcCenter; std::optional quantizedVolumeOffset; @@ -123,7 +126,6 @@ struct InstanceContent { std::optional scale; std::optional scaleNonUniform; std::optional batchId; - // batch table format? CesiumUtility::ErrorList errors; }; @@ -195,7 +197,7 @@ glm::quat rotationFromUpRight(const glm::vec3& up, const glm::vec3& right) { return upRot * rightRot; } -struct ConvertResult { +struct ConvertedI3dm { GltfConverterResult gltfResult; DecodedInstances decodedInstances; }; @@ -212,32 +214,32 @@ 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 convertInstancesContent( +CesiumAsync::Future convertI3dmContent( const gsl::span& instancesBinary, - const InstancesHeader& header, + const I3dmHeader& header, uint32_t headerLength, const CesiumGltfReader::GltfReaderOptions& options, const AssetFetcher& assetFetcher, GltfConverterResult& result) { - ConvertResult subResult; - DecodedInstances& decodedInstances = subResult.decodedInstances; - subResult.gltfResult = result; + ConvertedI3dm convertedI3dm; + DecodedInstances& decodedInstances = convertedI3dm.decodedInstances; + convertedI3dm.gltfResult = result; auto finishEarly = [&]() { - return assetFetcher.asyncSystem.createResolvedFuture(std::move(subResult)); + return assetFetcher.asyncSystem.createResolvedFuture(std::move(convertedI3dm)); }; if (header.featureTableJsonByteLength == 0 || header.featureTableBinaryByteLength == 0) { return finishEarly(); } - const uint32_t glTFStart = headerLength + header.featureTableJsonByteLength + + 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); + const uint32_t gltfEnd = header.byteLength; + auto gltfData = instancesBinary.subspan(gltfStart, gltfEnd - gltfStart); std::optional> assetFuture; auto featureTableJsonData = instancesBinary.subspan(headerLength, header.featureTableJsonByteLength); @@ -246,20 +248,20 @@ CesiumAsync::Future convertInstancesContent( reinterpret_cast(featureTableJsonData.data()), featureTableJsonData.size()); if (featureTableJson.HasParseError()) { - subResult.gltfResult.errors.emplaceError(fmt::format( + convertedI3dm.gltfResult.errors.emplaceError(fmt::format( "Error when parsing feature table JSON, error code {} at byte offset " "{}", featureTableJson.GetParseError(), featureTableJson.GetErrorOffset())); return finishEarly(); } - InstanceContent parsedContent; + I3dmContent parsedContent; // Global semantics - if (auto optinstancesLength = + if (auto optInstancesLength = getValue(featureTableJson, "INSTANCES_LENGTH")) { - parsedContent.instancesLength = *optinstancesLength; + parsedContent.instancesLength = *optInstancesLength; } else { - subResult.gltfResult.errors.emplaceError( + convertedI3dm.gltfResult.errors.emplaceError( "Error parsing I3DM feature table, no valid " "INSTANCES_LENGTH was found."); return finishEarly(); @@ -269,16 +271,17 @@ CesiumAsync::Future convertInstancesContent( decodedInstances.rtcCenter = parsedContent.rtcCenter; parsedContent.position = - parseOffset(featureTableJson, "POSITION", subResult.gltfResult.errors); + parseOffsetForSemantic( + featureTableJson, "POSITION", convertedI3dm.gltfResult.errors); if (!parsedContent.position) { - if (subResult.gltfResult.errors.hasErrors()) { + if (convertedI3dm.gltfResult.errors.hasErrors()) { return finishEarly(); } - parsedContent.positionQuantized = parseOffset( + parsedContent.positionQuantized = parseOffsetForSemantic( featureTableJson, "POSITION_QUANTIZED", - subResult.gltfResult.errors); - if (subResult.gltfResult.errors.hasErrors()) { + convertedI3dm.gltfResult.errors); + if (convertedI3dm.gltfResult.errors.hasErrors()) { return finishEarly(); } } @@ -286,17 +289,17 @@ CesiumAsync::Future convertInstancesContent( 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"); + convertedI3dm.gltfResult.errors.emplaceError( + "Error parsing I3DM feature table, the I3dm uses quatized positions " + "but has no valid QUANTIZED_VOLUME_OFFSET property"); return finishEarly(); } 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"); + convertedI3dm.gltfResult.errors.emplaceError( + "Error parsing I3DM feature table, the I3dm uses quatized positions " + "but has no valid QUANTIZED_VOLUME_SCALE property"); return finishEarly(); } } @@ -306,28 +309,31 @@ CesiumAsync::Future convertInstancesContent( decodedInstances.rotationENU = *optENU; } parsedContent.normalUp = - parseOffset(featureTableJson, "NORMAL_UP", subResult.gltfResult.errors); - parsedContent.normalRight = parseOffset( + parseOffsetForSemantic( + featureTableJson, "NORMAL_UP", convertedI3dm.gltfResult.errors); + parsedContent.normalRight = parseOffsetForSemantic( featureTableJson, "NORMAL_RIGHT", - subResult.gltfResult.errors); - parsedContent.normalUpOct32p = parseOffset( + convertedI3dm.gltfResult.errors); + parsedContent.normalUpOct32p = parseOffsetForSemantic( featureTableJson, "NORMAL_UP_OCT32P", - subResult.gltfResult.errors); - parsedContent.normalRightOct32p = parseOffset( + convertedI3dm.gltfResult.errors); + parsedContent.normalRightOct32p = parseOffsetForSemantic( featureTableJson, "NORMAL_RIGHT_OCT32P", - subResult.gltfResult.errors); + convertedI3dm.gltfResult.errors); parsedContent.scale = - parseOffset(featureTableJson, "SCALE", subResult.gltfResult.errors); - parsedContent.scaleNonUniform = parseOffset( + parseOffsetForSemantic( + featureTableJson, "SCALE", convertedI3dm.gltfResult.errors); + parsedContent.scaleNonUniform = parseOffsetForSemantic( featureTableJson, "SCALE_NON_UNIFORM", - subResult.gltfResult.errors); + convertedI3dm.gltfResult.errors); parsedContent.batchId = - parseOffset(featureTableJson, "BATCH_ID", subResult.gltfResult.errors); - if (subResult.gltfResult.errors.hasErrors()) { + parseOffsetForSemantic( + featureTableJson, "BATCH_ID", convertedI3dm.gltfResult.errors); + if (convertedI3dm.gltfResult.errors.hasErrors()) { return finishEarly(); } auto featureTableBinaryData = instancesBinary.subspan( @@ -416,10 +422,10 @@ CesiumAsync::Future convertInstancesContent( decodedInstances.scales[i] = rawScaleNonUniform[i]; } } - rebaseInstances(decodedInstances); + 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(gltfData.data()), gltfData.size()); @@ -438,24 +444,24 @@ CesiumAsync::Future convertInstancesContent( options, 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, assetFetcher) - .thenImmediately([subResult = std::move(subResult)]( + .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; }); } } @@ -593,7 +599,7 @@ void copyToBuffer( copyToBuffer(position, rotation, scale, &bufferData[i * totalStride]); } -void instantiateInstances( +void instantiateGltfInstances( GltfConverterResult& result, const DecodedInstances& decodedInstances) { std::set meshNodes; @@ -711,28 +717,28 @@ CesiumAsync::Future I3dmToGltfConverter::convert( const CesiumGltfReader::GltfReaderOptions& options, 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 assetFetcher.asyncSystem.createResolvedFuture(std::move(result)); } DecodedInstances decodedInstances; - return convertInstancesContent( + return convertI3dmContent( instancesBinary, header, headerLength, options, 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 diff --git a/Cesium3DTilesContent/src/LegacyUtilities.cpp b/Cesium3DTilesContent/src/LegacyUtilities.cpp index 4b2956133..2b1f48156 100644 --- a/Cesium3DTilesContent/src/LegacyUtilities.cpp +++ b/Cesium3DTilesContent/src/LegacyUtilities.cpp @@ -17,7 +17,7 @@ namespace Cesium3DTilesContent { namespace LegacyUtilities { using namespace CesiumGltf; -std::optional parseOffset( +std::optional parseOffsetForSemantic( const rapidjson::Document& document, const char* semantic, CesiumUtility::ErrorList& errorList) { @@ -77,19 +77,12 @@ parseArrayValueDVec3(const rapidjson::Document& document, const char* name) { return {}; } -int32_t createBufferInGltf(Model& gltf) { +int32_t createBufferInGltf(Model& gltf, std::vector buffer) { size_t bufferId = gltf.buffers.size(); Buffer& gltfBuffer = gltf.buffers.emplace_back(); - gltfBuffer.byteLength = 0; - return static_cast(bufferId); -} - -int32_t createBufferInGltf(Model& gltf, std::vector&& buffer) { - int32_t bufferId = createBufferInGltf(gltf); - Buffer& gltfBuffer = gltf.buffers[static_cast(bufferId)]; gltfBuffer.byteLength = static_cast(buffer.size()); gltfBuffer.cesium.data = std::move(buffer); - return bufferId; + return static_cast(bufferId); } int32_t createBufferViewInGltf( diff --git a/CesiumNativeTests/include/CesiumNativeTests/FileAccessor.h b/CesiumNativeTests/include/CesiumNativeTests/FileAccessor.h index 5b920ef14..9a68a3e18 100644 --- a/CesiumNativeTests/include/CesiumNativeTests/FileAccessor.h +++ b/CesiumNativeTests/include/CesiumNativeTests/FileAccessor.h @@ -15,7 +15,7 @@ class FileAccessor : public CesiumAsync::IAssetAccessor { CesiumAsync::Future> request( const CesiumAsync::AsyncSystem& asyncSystem, - const std::string& /* verb */, + const std::string& verb, const std::string& url, const std::vector& headers, const gsl::span&) override; From 598fd2e92fa65a6b89c9b4dceba22851f985b642 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Fri, 17 May 2024 15:54:09 +0200 Subject: [PATCH 21/32] Break i3dm JSON parsing out into a helper function --- .../src/I3dmToGltfConverter.cpp | 202 +++++++++++------- 1 file changed, 120 insertions(+), 82 deletions(-) diff --git a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp index b668a73c9..4dfab837b 100644 --- a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp @@ -217,43 +217,20 @@ struct ConvertedI3dm { + Future work: Metadata / feature id? */ -CesiumAsync::Future convertI3dmContent( - const gsl::span& 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> assetFuture; - auto featureTableJsonData = - instancesBinary.subspan(headerLength, header.featureTableJsonByteLength); +std::optional parseI3dmJson( + const gsl::span featureTableJsonData, + CesiumUtility::ErrorList& errors) { rapidjson::Document featureTableJson; featureTableJson.Parse( reinterpret_cast(featureTableJsonData.data()), featureTableJsonData.size()); if (featureTableJson.HasParseError()) { - convertedI3dm.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 {}; } I3dmContent parsedContent; // Global semantics @@ -261,88 +238,148 @@ CesiumAsync::Future convertI3dmContent( getValue(featureTableJson, "INSTANCES_LENGTH")) { parsedContent.instancesLength = *optInstancesLength; } else { - convertedI3dm.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 = parseOffsetForSemantic( - featureTableJson, "POSITION", convertedI3dm.gltfResult.errors); - if (!parsedContent.position) { - if (convertedI3dm.gltfResult.errors.hasErrors()) { - return finishEarly(); - } - parsedContent.positionQuantized = parseOffsetForSemantic( - featureTableJson, - "POSITION_QUANTIZED", - convertedI3dm.gltfResult.errors); - if (convertedI3dm.gltfResult.errors.hasErrors()) { - return finishEarly(); - } - } - if (parsedContent.positionQuantized) { + featureTableJson, "POSITION", errors); + parsedContent.positionQuantized = parseOffsetForSemantic( + featureTableJson, + "POSITION_QUANTIZED", + errors); + if (errors.hasErrors()) { + return {}; + } + // 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) { - convertedI3dm.gltfResult.errors.emplaceError( - "Error parsing I3DM feature table, the I3dm uses quatized positions " - "but has 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) { - convertedI3dm.gltfResult.errors.emplaceError( + 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 finishEarly(); + return {}; } } - decodedInstances.rotationENU = false; if (auto optENU = getValue(featureTableJson, "EAST_NORTH_UP")) { parsedContent.eastNorthUp = *optENU; - decodedInstances.rotationENU = *optENU; } - parsedContent.normalUp = - parseOffsetForSemantic( - featureTableJson, "NORMAL_UP", convertedI3dm.gltfResult.errors); + parsedContent.normalUp = parseOffsetForSemantic( + featureTableJson, + "NORMAL_UP", + errors); parsedContent.normalRight = parseOffsetForSemantic( featureTableJson, "NORMAL_RIGHT", - convertedI3dm.gltfResult.errors); + 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", - convertedI3dm.gltfResult.errors); + errors); parsedContent.normalRightOct32p = parseOffsetForSemantic( featureTableJson, "NORMAL_RIGHT_OCT32P", - convertedI3dm.gltfResult.errors); - parsedContent.scale = - parseOffsetForSemantic( - featureTableJson, "SCALE", convertedI3dm.gltfResult.errors); + 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", - convertedI3dm.gltfResult.errors); - parsedContent.batchId = - parseOffsetForSemantic( - featureTableJson, "BATCH_ID", convertedI3dm.gltfResult.errors); - if (convertedI3dm.gltfResult.errors.hasErrors()) { + errors); + parsedContent.batchId = parseOffsetForSemantic( + featureTableJson, + "BATCH_ID", + errors); + if (errors.hasErrors()) { + return {}; + } + return parsedContent; +} + +CesiumAsync::Future convertI3dmContent( + const gsl::span& 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> assetFuture; + auto featureTableJsonData = + instancesBinary.subspan(headerLength, header.featureTableJsonByteLength); + std::optional 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) { + if (parsedContent.position.has_value()) { const auto* rawPosition = reinterpret_cast( featureTableBinaryData.data() + *parsedContent.position); for (unsigned i = 0; i < parsedContent.instancesLength; ++i) { @@ -367,7 +404,7 @@ CesiumAsync::Future convertI3dmContent( decodedInstances.rotations.resize( parsedContent.instancesLength, glm::quat(1.0f, 0.0f, 0.0f, 0.0f)); - if (parsedContent.normalUp && parsedContent.normalRight) { + if (parsedContent.normalUp.has_value() && parsedContent.normalRight.has_value()) { const auto* rawUp = reinterpret_cast( featureTableBinaryData.data() + *parsedContent.normalUp); const auto* rawRight = reinterpret_cast( @@ -376,7 +413,7 @@ CesiumAsync::Future convertI3dmContent( decodedInstances.rotations[i] = rotationFromUpRight(rawUp[i], rawRight[i]); } - } else if (parsedContent.normalUpOct32p && parsedContent.normalRightOct32p) { + } else if (parsedContent.normalUpOct32p.has_value() && parsedContent.normalRightOct32p.has_value()) { const auto* rawUpOct = reinterpret_cast( featureTableBinaryData.data() + *parsedContent.normalUpOct32p); const auto* rawRightOct = reinterpret_cast( @@ -408,18 +445,19 @@ CesiumAsync::Future convertI3dmContent( decodedInstances.scales.resize( parsedContent.instancesLength, glm::vec3(1.0, 1.0, 1.0)); - if (parsedContent.scale) { + if (parsedContent.scale.has_value()) { const auto* rawScale = reinterpret_cast( 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) { + } + if (parsedContent.scaleNonUniform.has_value()) { const auto* rawScaleNonUniform = reinterpret_cast( featureTableBinaryData.data() + *parsedContent.scaleNonUniform); for (unsigned i = 0; i < parsedContent.instancesLength; ++i) { - decodedInstances.scales[i] = rawScaleNonUniform[i]; + decodedInstances.scales[i] *= rawScaleNonUniform[i]; } } repositionInstances(decodedInstances); From 7369873a4028174ce0e248e4a653d467b498dba8 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Fri, 17 May 2024 16:43:35 +0200 Subject: [PATCH 22/32] Rework applyRTC and meshGpuTransforms Rename and rework arguments in response to review comments. --- .../Cesium3DTilesContent/LegacyUtilities.h | 2 +- .../src/I3dmToGltfConverter.cpp | 20 ++++++++++++------- Cesium3DTilesContent/src/LegacyUtilities.cpp | 2 +- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h index 340fde579..9f9cbcb0f 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h @@ -91,7 +91,7 @@ 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 GlmType toGlm(const GLTFType& gltfVal); diff --git a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp index 4dfab837b..47a03f64a 100644 --- a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp @@ -519,10 +519,10 @@ composeInstanceTransform(size_t i, const DecodedInstances& decodedInstances) { return result; } -std::vector meshGpuInstances( - GltfConverterResult& result, - const ExtensionExtMeshGpuInstancing& gpuExt) { - const Model& model = *result.model; +std::vector getMeshGpuInstancingTransforms( + const Model& model, + const ExtensionExtMeshGpuInstancing& gpuExt, + CesiumUtility::ErrorList& errors) { std::vector instances; if (gpuExt.attributes.empty()) { return instances; @@ -535,7 +535,7 @@ std::vector meshGpuInstances( return nullptr; }; auto errorOut = [&](const std::string& errorMsg) { - result.errors.emplaceError(errorMsg); + errors.emplaceError(errorMsg); return instances; }; @@ -672,7 +672,10 @@ void instantiateGltfInstances( 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::max()) { result.errors.emplaceError(fmt::format( @@ -682,6 +685,9 @@ void instantiateGltfInstances( return; } } + if (result.errors.hasErrors()) { + return; + } const uint32_t numNewInstances = static_cast(numInstances * existingInstances.size()); const size_t instanceDataSize = totalStride * numNewInstances; @@ -742,7 +748,7 @@ void instantiateGltfInstances( gpuExt.attributes["SCALE"] = scaleAccessorId; }); if (decodedInstances.rtcCenter) { - applyRTC(*result.model, *decodedInstances.rtcCenter); + applyRtcToNodes(*result.model, *decodedInstances.rtcCenter); } instanceBuffer.byteLength = static_cast(instanceBuffer.cesium.data.size()); diff --git a/Cesium3DTilesContent/src/LegacyUtilities.cpp b/Cesium3DTilesContent/src/LegacyUtilities.cpp index 2b1f48156..c49c6b55b 100644 --- a/Cesium3DTilesContent/src/LegacyUtilities.cpp +++ b/Cesium3DTilesContent/src/LegacyUtilities.cpp @@ -118,7 +118,7 @@ int32_t createAccessorInGltf( return static_cast(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); From 88a3b8dfb731cc2802f8520da17bd36de098d616 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Fri, 17 May 2024 18:35:08 +0200 Subject: [PATCH 23/32] Use gsl:span instead of raw pointers --- .../src/I3dmToGltfConverter.cpp | 127 ++++++++++-------- 1 file changed, 74 insertions(+), 53 deletions(-) diff --git a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp index 47a03f64a..7746bf32d 100644 --- a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp @@ -376,53 +376,66 @@ CesiumAsync::Future convertI3dmContent( auto featureTableBinaryData = instancesBinary.subspan( headerLength + header.featureTableJsonByteLength, header.featureTableBinaryByteLength); - decodedInstances.positions.resize( - parsedContent.instancesLength, - glm::vec3(0.0f, 0.0f, 0.0f)); + 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()) { - const auto* rawPosition = reinterpret_cast( - featureTableBinaryData.data() + *parsedContent.position); - for (unsigned i = 0; i < parsedContent.instancesLength; ++i) { - decodedInstances.positions[i] += rawPosition[i]; - } + gsl::span rawPositions(reinterpret_cast( + binaryData + *parsedContent.position), + numInstances); + decodedInstances.positions.assign(rawPositions.begin(), rawPositions.end()); } else { - const auto* rawQPosition = reinterpret_cast( - 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( - (*posQuantized)[j] / 65535.0 * + gsl::span rawQPositions(reinterpret_cast( + 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( + 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.has_value() && parsedContent.normalRight.has_value()) { - const auto* rawUp = reinterpret_cast( - featureTableBinaryData.data() + *parsedContent.normalUp); - const auto* rawRight = reinterpret_cast( - featureTableBinaryData.data() + *parsedContent.normalRight); - for (unsigned i = 0; i < parsedContent.instancesLength; ++i) { - decodedInstances.rotations[i] = - rotationFromUpRight(rawUp[i], rawRight[i]); - } + gsl::span rawUp(reinterpret_cast( + binaryData + *parsedContent.normalUp), + numInstances); + gsl::span rawRight(reinterpret_cast( + 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()) { - const auto* rawUpOct = reinterpret_cast( - featureTableBinaryData.data() + *parsedContent.normalUpOct32p); - const auto* rawRightOct = reinterpret_cast( - 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); - } + + gsl::span rawUpOct(reinterpret_cast( + binaryData + *parsedContent.normalUpOct32p), + numInstances); + gsl::span rawRightOct(reinterpret_cast( + 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 = assetFetcher.tileTransform; if (decodedInstances.rtcCenter) { @@ -442,23 +455,31 @@ CesiumAsync::Future convertI3dmContent( decodedInstances.rotations[i] = tileFrameRot; } } - decodedInstances.scales.resize( - parsedContent.instancesLength, - glm::vec3(1.0, 1.0, 1.0)); + decodedInstances.scales.resize(numInstances, glm::vec3(1.0, 1.0, 1.0)); if (parsedContent.scale.has_value()) { - const auto* rawScale = reinterpret_cast( - featureTableBinaryData.data() + *parsedContent.scale); - for (unsigned i = 0; i < parsedContent.instancesLength; ++i) { - decodedInstances.scales[i] = - glm::vec3(rawScale[i], rawScale[i], rawScale[i]); - } + gsl::span rawScales(reinterpret_cast( + binaryData + *parsedContent.scale), + numInstances); + std::transform(rawScales.begin(), + rawScales.end(), + decodedInstances.scales.begin(), + [](float scaleVal) { + return glm::vec3(scaleVal); + }); } if (parsedContent.scaleNonUniform.has_value()) { - const auto* rawScaleNonUniform = reinterpret_cast( - featureTableBinaryData.data() + *parsedContent.scaleNonUniform); - for (unsigned i = 0; i < parsedContent.instancesLength; ++i) { - decodedInstances.scales[i] *= rawScaleNonUniform[i]; - } + gsl::span rawScalesNonUniform( + reinterpret_cast( + 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; From be323a4887da0182b0a7d64bc76a57da0b891b68 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Fri, 17 May 2024 19:30:45 +0200 Subject: [PATCH 24/32] Move rotation function to CesiumUtility/Math.h --- .../src/I3dmToGltfConverter.cpp | 24 +-------------- CesiumUtility/include/CesiumUtility/Math.h | 30 ++++++++++++++++++- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp index 57127a89f..82c5b91be 100644 --- a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp @@ -147,31 +147,9 @@ glm::vec3 decodeOct32P(const uint16_t rawOct[2]) { calculation of the second rotation must take place within the first frame. The rotations are calculated by finding the rotation that takes one vector to - another. If we take the dot and cross products of the two vectors and store - them in a quaternion, that quaternion represents twice the required rotation. - We get the correct quaternion by "averaging" with the zero rotation - quaternion, in a way analagous to finding the half vector between two 3D - vectors. + another. */ -glm::quat rotation(const glm::vec3& vec1, const glm::vec3& vec2) { - float cosRot = dot(vec1, vec2); - // Not using epsilon for these tests. If abs(cosRot) < 1.0, we can still - // create a sensible rotation. - if (cosRot >= 1.0f) { - // zero rotation - return glm::quat(1.0, 0.0f, 0.0f, 0.0f); - } - if (cosRot <= -1.0f) { - auto rotAxis = CesiumUtility::Math::perpVec(vec1); - // rotation by pi radians - return glm::quat(0.0f, rotAxis); - } - auto rotAxis = cross(vec1, vec2); - glm::quat sumQuat(cosRot + 1.0f, rotAxis); - return normalize(sumQuat); -} - glm::quat rotationFromUpRight(const glm::vec3& up, const glm::vec3& right) { // First rotation: up auto upRot = rotation(glm::vec3(0.0f, 1.0f, 0.0f), up); diff --git a/CesiumUtility/include/CesiumUtility/Math.h b/CesiumUtility/include/CesiumUtility/Math.h index ff055ac21..1207c03e1 100644 --- a/CesiumUtility/include/CesiumUtility/Math.h +++ b/CesiumUtility/include/CesiumUtility/Math.h @@ -452,7 +452,7 @@ class CESIUMUTILITY_API Math final { } } - /** + /** * @brief Construct a vector perpendicular to the argument. * @param v input vector * @return a perpendicular vector @@ -467,6 +467,34 @@ class CESIUMUTILITY_API Math final { return glm::vec<3, T, Q>( 0, v.z, -v.y) / std::sqrt(v.y * v.y + v.z * v.z); } + /** @brief Compute the rotation between two vectors + * @param vec1 first vector + * @param vec2 second vector + * @return quaternion representing the rotation of vec1 to vec2 + */ + template + static glm::qua rotation(const glm::vec<3, T, Q>& vec1, const glm::vec<3, T, Q>& vec2) { + // If we take the dot and cross products of the two vectors and store + // them in a quaternion, that quaternion represents twice the required rotation. + // We get the correct quaternion by "averaging" with the zero rotation + // quaternion, in a way analagous to finding the half vector between two 3D + // vectors. + auto cosRot = dot(vec1, vec2); + // Not using epsilon for these tests. If abs(cosRot) < 1.0, we can still + // create a sensible rotation. + if (cosRot >= 1) { + // zero rotation + return glm::qua(1, 0, 0, 0); + } + if (cosRot <= -1) { + auto rotAxis = CesiumUtility::Math::perpVec(vec1); + // rotation by pi radians + return glm::qua(0, rotAxis); + } + auto rotAxis = cross(vec1, vec2); + glm::qua sumQuat(cosRot + 1, rotAxis); + return normalize(sumQuat); +} }; } // namespace CesiumUtility From e60e936e678b5fc8b03498138165db1c16d884de Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Fri, 17 May 2024 19:42:22 +0200 Subject: [PATCH 25/32] format fixes --- .../Cesium3DTilesContent/GltfConverters.h | 2 +- .../Cesium3DTilesContent/LegacyUtilities.h | 3 +- Cesium3DTilesContent/src/GltfConverters.cpp | 2 +- .../src/I3dmToGltfConverter.cpp | 201 +++++++++--------- .../test/ConvertTileToGltf.cpp | 3 +- CesiumUtility/include/CesiumUtility/Math.h | 45 ++-- 6 files changed, 129 insertions(+), 127 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h index decef5ff6..651446e18 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h @@ -42,7 +42,7 @@ struct CESIUM3DTILESCONTENT_API AssetFetcher { const CesiumAsync::AsyncSystem& asyncSystem; const std::shared_ptr pAssetAccessor; const std::string baseUrl; - glm::dmat4 tileTransform; // For ENU transforms in i3dm + glm::dmat4 tileTransform; // For ENU transforms in i3dm const std::vector& requestHeaders; }; diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h index 9f9cbcb0f..15a12b041 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h @@ -76,7 +76,8 @@ parseArrayValueDVec3(const rapidjson::Value& arrayValue); std::optional parseArrayValueDVec3(const rapidjson::Document& document, const char* name); -int32_t createBufferInGltf(CesiumGltf::Model& gltf, std::vector buffer = {}); +int32_t +createBufferInGltf(CesiumGltf::Model& gltf, std::vector buffer = {}); int32_t createBufferViewInGltf( CesiumGltf::Model& gltf, diff --git a/Cesium3DTilesContent/src/GltfConverters.cpp b/Cesium3DTilesContent/src/GltfConverters.cpp index caa8b263c..4872bc910 100644 --- a/Cesium3DTilesContent/src/GltfConverters.cpp +++ b/Cesium3DTilesContent/src/GltfConverters.cpp @@ -1,5 +1,5 @@ -#include #include +#include #include #include diff --git a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp index ace358a46..4617b8521 100644 --- a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp @@ -193,10 +193,10 @@ std::optional parseI3dmJson( featureTableJsonData.size()); if (featureTableJson.HasParseError()) { errors.emplaceError(fmt::format( - "Error when parsing feature table JSON, error code {} at byte offset " - "{}", - featureTableJson.GetParseError(), - featureTableJson.GetErrorOffset())); + "Error when parsing feature table JSON, error code {} at byte offset " + "{}", + featureTableJson.GetParseError(), + featureTableJson.GetErrorOffset())); return {}; } I3dmContent parsedContent; @@ -205,37 +205,35 @@ std::optional parseI3dmJson( getValue(featureTableJson, "INSTANCES_LENGTH")) { parsedContent.instancesLength = *optInstancesLength; } else { - errors.emplaceError( - "Error parsing I3DM feature table, no valid INSTANCES_LENGTH was found."); + errors.emplaceError("Error parsing I3DM feature table, no valid " + "INSTANCES_LENGTH was found."); return {}; } parsedContent.rtcCenter = parseArrayValueDVec3(featureTableJson, "RTC_CENTER"); parsedContent.position = - parseOffsetForSemantic( - featureTableJson, "POSITION", errors); - parsedContent.positionQuantized = parseOffsetForSemantic( - featureTableJson, - "POSITION_QUANTIZED", - errors); + parseOffsetForSemantic(featureTableJson, "POSITION", errors); + parsedContent.positionQuantized = + parseOffsetForSemantic(featureTableJson, "POSITION_QUANTIZED", errors); if (errors.hasErrors()) { return {}; } // 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."); + 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.has_value()) { - errors.emplaceError( - "Error parsing I3DM feature table, the I3dm uses quatized positions " - "but has no valid QUANTIZED_VOLUME_OFFSET property"); - return {}; + 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"); @@ -249,56 +247,48 @@ std::optional parseI3dmJson( if (auto optENU = getValue(featureTableJson, "EAST_NORTH_UP")) { parsedContent.eastNorthUp = *optENU; } - parsedContent.normalUp = parseOffsetForSemantic( - featureTableJson, - "NORMAL_UP", - errors); - parsedContent.normalRight = parseOffsetForSemantic( - featureTableJson, - "NORMAL_RIGHT", - errors); + parsedContent.normalUp = + parseOffsetForSemantic(featureTableJson, "NORMAL_UP", errors); + parsedContent.normalRight = + parseOffsetForSemantic(featureTableJson, "NORMAL_RIGHT", errors); if (errors.hasErrors()) { return {}; } - if (parsedContent.normalUp.has_value() && !parsedContent.normalRight.has_value()) { + 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()) { + 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", - errors); - parsedContent.normalRightOct32p = parseOffsetForSemantic( - featureTableJson, - "NORMAL_RIGHT_OCT32P", - errors); + parsedContent.normalUpOct32p = + parseOffsetForSemantic(featureTableJson, "NORMAL_UP_OCT32P", errors); + parsedContent.normalRightOct32p = + parseOffsetForSemantic(featureTableJson, "NORMAL_RIGHT_OCT32P", 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."); + 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."); + 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", - errors); - parsedContent.batchId = parseOffsetForSemantic( - featureTableJson, - "BATCH_ID", - errors); + parsedContent.scale = + parseOffsetForSemantic(featureTableJson, "SCALE", errors); + parsedContent.scaleNonUniform = + parseOffsetForSemantic(featureTableJson, "SCALE_NON_UNIFORM", errors); + parsedContent.batchId = + parseOffsetForSemantic(featureTableJson, "BATCH_ID", errors); if (errors.hasErrors()) { return {}; } @@ -316,7 +306,8 @@ CesiumAsync::Future convertI3dmContent( DecodedInstances& decodedInstances = convertedI3dm.decodedInstances; convertedI3dm.gltfResult = result; auto finishEarly = [&]() { - return assetFetcher.asyncSystem.createResolvedFuture(std::move(convertedI3dm)); + return assetFetcher.asyncSystem.createResolvedFuture( + std::move(convertedI3dm)); }; if (header.featureTableJsonByteLength == 0 || header.featureTableBinaryByteLength == 0) { @@ -347,14 +338,16 @@ CesiumAsync::Future convertI3dmContent( 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 rawPositions(reinterpret_cast( - binaryData + *parsedContent.position), - numInstances); + gsl::span rawPositions( + reinterpret_cast( + binaryData + *parsedContent.position), + numInstances); decodedInstances.positions.assign(rawPositions.begin(), rawPositions.end()); } else { - gsl::span rawQPositions(reinterpret_cast( - binaryData + *parsedContent.positionQuantized), - numInstances); + gsl::span rawQPositions( + reinterpret_cast( + binaryData + *parsedContent.positionQuantized), + numInstances); std::transform( rawQPositions.begin(), rawQPositions.end(), @@ -364,7 +357,7 @@ CesiumAsync::Future convertI3dmContent( for (unsigned j = 0; j < 3; ++j) { position[j] = static_cast( posQuantized[j] / 65535.0 * - (*parsedContent.quantizedVolumeScale)[j] + + (*parsedContent.quantizedVolumeScale)[j] + (*parsedContent.quantizedVolumeOffset)[j]); } return position; @@ -373,36 +366,45 @@ CesiumAsync::Future convertI3dmContent( decodedInstances.rotations.resize( numInstances, glm::quat(1.0f, 0.0f, 0.0f, 0.0f)); - if (parsedContent.normalUp.has_value() && parsedContent.normalRight.has_value()) { - gsl::span rawUp(reinterpret_cast( - binaryData + *parsedContent.normalUp), - numInstances); - gsl::span rawRight(reinterpret_cast( - 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 rawUpOct(reinterpret_cast( - binaryData + *parsedContent.normalUpOct32p), - numInstances); - gsl::span rawRightOct(reinterpret_cast( - 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)); - }); + if (parsedContent.normalUp.has_value() && + parsedContent.normalRight.has_value()) { + gsl::span rawUp( + reinterpret_cast( + binaryData + *parsedContent.normalUp), + numInstances); + gsl::span rawRight( + reinterpret_cast( + 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 rawUpOct( + reinterpret_cast( + binaryData + *parsedContent.normalUpOct32p), + numInstances); + gsl::span rawRightOct( + reinterpret_cast( + 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 = assetFetcher.tileTransform; if (decodedInstances.rtcCenter) { @@ -424,15 +426,14 @@ CesiumAsync::Future convertI3dmContent( } decodedInstances.scales.resize(numInstances, glm::vec3(1.0, 1.0, 1.0)); if (parsedContent.scale.has_value()) { - gsl::span rawScales(reinterpret_cast( - binaryData + *parsedContent.scale), - numInstances); - std::transform(rawScales.begin(), - rawScales.end(), - decodedInstances.scales.begin(), - [](float scaleVal) { - return glm::vec3(scaleVal); - }); + gsl::span rawScales( + reinterpret_cast(binaryData + *parsedContent.scale), + numInstances); + std::transform( + rawScales.begin(), + rawScales.end(), + decodedInstances.scales.begin(), + [](float scaleVal) { return glm::vec3(scaleVal); }); } if (parsedContent.scaleNonUniform.has_value()) { gsl::span rawScalesNonUniform( diff --git a/Cesium3DTilesContent/test/ConvertTileToGltf.cpp b/Cesium3DTilesContent/test/ConvertTileToGltf.cpp index 159a7c378..d74128258 100644 --- a/Cesium3DTilesContent/test/ConvertTileToGltf.cpp +++ b/Cesium3DTilesContent/test/ConvertTileToGltf.cpp @@ -9,8 +9,7 @@ namespace Cesium3DTilesContent { CesiumAsync::AsyncSystem ConvertTileToGltf::asyncSystem( std::make_shared()); -AssetFetcher -ConvertTileToGltf::makeAssetFetcher(const std::string& baseUrl) { +AssetFetcher ConvertTileToGltf::makeAssetFetcher(const std::string& baseUrl) { auto fileAccessor = std::make_shared(); std::vector requestHeaders; return AssetFetcher( diff --git a/CesiumUtility/include/CesiumUtility/Math.h b/CesiumUtility/include/CesiumUtility/Math.h index 1207c03e1..271e3da05 100644 --- a/CesiumUtility/include/CesiumUtility/Math.h +++ b/CesiumUtility/include/CesiumUtility/Math.h @@ -464,7 +464,7 @@ class CESIUMUTILITY_API Math final { if (std::abs(v.x) > std::abs(v.y)) { return glm::vec<3, T, Q>(-v.z, 0, v.x) / std::sqrt(v.x * v.x + v.z * v.z); } - return glm::vec<3, T, Q>( 0, v.z, -v.y) / std::sqrt(v.y * v.y + v.z * v.z); + return glm::vec<3, T, Q>(0, v.z, -v.y) / std::sqrt(v.y * v.y + v.z * v.z); } /** @brief Compute the rotation between two vectors @@ -473,28 +473,29 @@ class CESIUMUTILITY_API Math final { * @return quaternion representing the rotation of vec1 to vec2 */ template - static glm::qua rotation(const glm::vec<3, T, Q>& vec1, const glm::vec<3, T, Q>& vec2) { - // If we take the dot and cross products of the two vectors and store - // them in a quaternion, that quaternion represents twice the required rotation. - // We get the correct quaternion by "averaging" with the zero rotation - // quaternion, in a way analagous to finding the half vector between two 3D - // vectors. - auto cosRot = dot(vec1, vec2); - // Not using epsilon for these tests. If abs(cosRot) < 1.0, we can still - // create a sensible rotation. - if (cosRot >= 1) { - // zero rotation - return glm::qua(1, 0, 0, 0); - } - if (cosRot <= -1) { - auto rotAxis = CesiumUtility::Math::perpVec(vec1); - // rotation by pi radians - return glm::qua(0, rotAxis); + static glm::qua + rotation(const glm::vec<3, T, Q>& vec1, const glm::vec<3, T, Q>& vec2) { + // If we take the dot and cross products of the two vectors and store + // them in a quaternion, that quaternion represents twice the required + // rotation. We get the correct quaternion by "averaging" with the zero + // rotation quaternion, in a way analagous to finding the half vector + // between two 3D vectors. + auto cosRot = dot(vec1, vec2); + // Not using epsilon for these tests. If abs(cosRot) < 1.0, we can still + // create a sensible rotation. + if (cosRot >= 1) { + // zero rotation + return glm::qua(1, 0, 0, 0); + } + if (cosRot <= -1) { + auto rotAxis = CesiumUtility::Math::perpVec(vec1); + // rotation by pi radians + return glm::qua(0, rotAxis); + } + auto rotAxis = cross(vec1, vec2); + glm::qua sumQuat(cosRot + 1, rotAxis); + return normalize(sumQuat); } - auto rotAxis = cross(vec1, vec2); - glm::qua sumQuat(cosRot + 1, rotAxis); - return normalize(sumQuat); -} }; } // namespace CesiumUtility From edc22cb5d5a6ebef19814549f95f25dd682686c0 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Mon, 20 May 2024 15:40:18 +0200 Subject: [PATCH 26/32] Test compilation and gcc fixes --- Cesium3DTilesContent/src/I3dmToGltfConverter.cpp | 4 ++-- Cesium3DTilesContent/test/ConvertTileToGltf.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp index 4617b8521..8d55c3d03 100644 --- a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp @@ -352,7 +352,7 @@ CesiumAsync::Future convertI3dmContent( rawQPositions.begin(), rawQPositions.end(), decodedInstances.positions.begin(), - [&parsedContent](const auto&& posQuantized) { + [&parsedContent](auto&& posQuantized) { glm::vec3 position; for (unsigned j = 0; j < 3; ++j) { position[j] = static_cast( @@ -400,7 +400,7 @@ CesiumAsync::Future convertI3dmContent( rawUpOct.end(), rawRightOct.begin(), decodedInstances.rotations.begin(), - [](const auto&& upOct, const auto&& rightOct) { + [](auto&& upOct, auto&& rightOct) { return rotationFromUpRight( decodeOct32P(upOct), decodeOct32P(rightOct)); diff --git a/Cesium3DTilesContent/test/ConvertTileToGltf.h b/Cesium3DTilesContent/test/ConvertTileToGltf.h index 7517f04c5..12215c308 100644 --- a/Cesium3DTilesContent/test/ConvertTileToGltf.h +++ b/Cesium3DTilesContent/test/ConvertTileToGltf.h @@ -22,6 +22,6 @@ class ConvertTileToGltf { private: static CesiumAsync::AsyncSystem asyncSystem; - static ConverterSubprocessor makeSubprocessor(const std::string& baseUrl); + static AssetFetcher makeAssetFetcher(const std::string& baseUrl); }; } // namespace Cesium3DTilesContent From 9af4cd7fa1b985ecd0dab925cca7155e0c9a5a37 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Thu, 23 May 2024 16:46:29 +0200 Subject: [PATCH 27/32] Add tests for CesiumUtility::Math::rotation and fix a bug --- .../include/CesiumNativeTests/RandomVector.h | 52 +++++++++++++++++++ CesiumUtility/include/CesiumUtility/Math.h | 24 +++++---- CesiumUtility/test/TestMath.cpp | 38 ++++++++++++++ 3 files changed, 103 insertions(+), 11 deletions(-) create mode 100644 CesiumNativeTests/include/CesiumNativeTests/RandomVector.h diff --git a/CesiumNativeTests/include/CesiumNativeTests/RandomVector.h b/CesiumNativeTests/include/CesiumNativeTests/RandomVector.h new file mode 100644 index 000000000..42bd5a0be --- /dev/null +++ b/CesiumNativeTests/include/CesiumNativeTests/RandomVector.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include + +namespace CesiumNativeTests { + +// Produce a random N-dimensional unit vector. Use a constant seed in order to +// get a repeatable stream of vectors that can then be debugged! +template +struct RandomUnitVectorGenerator { + using value_type = typename Vec::value_type; + std::mt19937 gen; + std::uniform_real_distribution dis; + + RandomUnitVectorGenerator() + : dis( + std::nextafter(static_cast(-1), value_type()), + static_cast(1)) { + gen.seed(42); + } + + Vec operator()() { + Vec result(0); + value_type length2 = 0; + // If we don't discard values that fall outside the unit sphere, then the + // points are biased towards the corners of the unit cube. + do { + for (glm::length_t i = 0; i < Vec::length(); ++i) { + result[i] = dis(gen); + } + length2 = dot(result, result); + } while (length2 > 1 || length2 == 0); + return result / std::sqrt(length2); + } +}; + +template +struct RandomQuaternionGenerator { + RandomUnitVectorGenerator> vecGenerator; + + glm::qua operator()() { + glm::vec<4, T> vec = vecGenerator(); + return glm::qua(vec.w, vec.x, vec.y, vec.z); + } +}; +} // namespace CesiumNativeTests diff --git a/CesiumUtility/include/CesiumUtility/Math.h b/CesiumUtility/include/CesiumUtility/Math.h index 271e3da05..ded8212d9 100644 --- a/CesiumUtility/include/CesiumUtility/Math.h +++ b/CesiumUtility/include/CesiumUtility/Math.h @@ -454,8 +454,8 @@ class CESIUMUTILITY_API Math final { /** * @brief Construct a vector perpendicular to the argument. - * @param v input vector - * @return a perpendicular vector + * @param v The input vector + * @return A vector perpendicular to the input vector */ template static glm::vec<3, T, Q> perpVec(const glm::vec<3, T, Q>& v) { @@ -467,10 +467,10 @@ class CESIUMUTILITY_API Math final { return glm::vec<3, T, Q>(0, v.z, -v.y) / std::sqrt(v.y * v.y + v.z * v.z); } - /** @brief Compute the rotation between two vectors - * @param vec1 first vector - * @param vec2 second vector - * @return quaternion representing the rotation of vec1 to vec2 + /** @brief Compute the rotation between two unit vectors. + * @param vec1 The first vector. + * @param vec2 The second vector. + * @return A quaternion representing the rotation of vec1 to vec2. */ template static glm::qua @@ -481,18 +481,20 @@ class CESIUMUTILITY_API Math final { // rotation quaternion, in a way analagous to finding the half vector // between two 3D vectors. auto cosRot = dot(vec1, vec2); + auto rotAxis = cross(vec1, vec2); + auto rotAxisLen2 = dot(rotAxis, rotAxis); // Not using epsilon for these tests. If abs(cosRot) < 1.0, we can still // create a sensible rotation. - if (cosRot >= 1) { + if (cosRot >= 1 || (rotAxisLen2 == 0 && cosRot > 0)) { // zero rotation return glm::qua(1, 0, 0, 0); } - if (cosRot <= -1) { - auto rotAxis = CesiumUtility::Math::perpVec(vec1); + if (cosRot <= -1 || (rotAxisLen2 == 0 && cosRot < 0)) { + auto perpAxis = CesiumUtility::Math::perpVec(vec1); // rotation by pi radians - return glm::qua(0, rotAxis); + return glm::qua(0, perpAxis); } - auto rotAxis = cross(vec1, vec2); + glm::qua sumQuat(cosRot + 1, rotAxis); return normalize(sumQuat); } diff --git a/CesiumUtility/test/TestMath.cpp b/CesiumUtility/test/TestMath.cpp index dc55f03a2..e84db887b 100644 --- a/CesiumUtility/test/TestMath.cpp +++ b/CesiumUtility/test/TestMath.cpp @@ -1,3 +1,4 @@ +#include "CesiumNativeTests/RandomVector.h" #include "CesiumUtility/Math.h" #include @@ -157,3 +158,40 @@ TEST_CASE("Math::perpVec") { glm::vec3 mutual1 = glm::cross(v1, perp1); CHECK(Math::equalsEpsilon(length(v1), length(mutual1), Math::Epsilon5)); } + +TEST_CASE("Math::rotation") { + CesiumNativeTests::RandomUnitVectorGenerator generator; + for (int i = 0; i < 100; ++i) { + glm::vec3 vec1 = generator(); + glm::vec3 vec2 = generator(); + glm::quat rotation = Math::rotation(vec1, vec2); + // Not a unit vector! + glm::vec3 axis(rotation.x, rotation.y, rotation.z); + // Is the rotation axis perpendicular to vec1 and vec2? + CHECK(Math::equalsEpsilon(dot(vec1, axis), 0.0f, Math::Epsilon5)); + CHECK(Math::equalsEpsilon(dot(vec2, axis), 0.0f, Math::Epsilon5)); + // Does the quaternion match the trig values we get with dot and cross? + const float c = dot(vec1, vec2); + const float s = length(cross(vec1, vec2)); + const float qc = rotation.w; + const float qs = length(axis); + // Double angle formulae + float testSin = 2.0f * qs * qc; + float testCos = qc * qc - qs * qs; + CHECK(Math::equalsEpsilon(s, testSin, Math::Epsilon5)); + CHECK(Math::equalsEpsilon(c, testCos, Math::Epsilon5)); + } + for (int i = 0; i < 100; ++i) { + glm::vec3 vec = generator(); + glm::quat rotation = Math::rotation(vec, vec); + CHECK(Math::equalsEpsilon(rotation.w, 1.0f, Math::Epsilon5)); + } + for (int i = 0; i < 100; ++i) { + glm::vec3 vec1 = generator(); + glm::vec3 vec2 = vec1 * -1.0f; + glm::quat rotation = Math::rotation(vec1, vec2); + glm::vec3 axis(rotation.x, rotation.y, rotation.z); + CHECK(Math::equalsEpsilon(rotation.w, 0.0f, Math::Epsilon5)); + CHECK(Math::equalsEpsilon(dot(vec1, axis), 0.0f, Math::Epsilon5)); + } +} From d822cf481e49ed293932142151c2e44d15c73ba0 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Thu, 23 May 2024 17:25:11 +0200 Subject: [PATCH 28/32] Use a fail() local function in ImplicitOctreeLoader This follows the change in ImplicitQuadtreeLoader. --- Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp index 04954b271..a67d41aad 100644 --- a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp @@ -118,14 +118,17 @@ CesiumAsync::Future requestTileContent( pCompletedRequest) mutable { const CesiumAsync::IAssetResponse* pResponse = pCompletedRequest->response(); + auto fail = [&]() { + return asyncSystem.createResolvedFuture( + TileLoadResult::createFailedResult(std::move(pCompletedRequest))); + }; const std::string& tileUrl = pCompletedRequest->url(); if (!pResponse) { SPDLOG_LOGGER_ERROR( pLogger, "Did not receive a valid response for tile content {}", tileUrl); - return asyncSystem.createResolvedFuture( - TileLoadResult::createFailedResult(std::move(pCompletedRequest))); + return fail(); } uint16_t statusCode = pResponse->statusCode(); @@ -135,8 +138,7 @@ CesiumAsync::Future requestTileContent( "Received status code {} for tile content {}", statusCode, tileUrl); - return asyncSystem.createResolvedFuture( - TileLoadResult::createFailedResult(std::move(pCompletedRequest))); + return fail(); } // find gltf converter @@ -180,8 +182,7 @@ CesiumAsync::Future requestTileContent( }); } // content type is not supported - return asyncSystem.createResolvedFuture( - TileLoadResult::createFailedResult(std::move(pCompletedRequest))); + return fail(); }); } } // namespace From 029d0aba7a7365fce52f46008e1d9b6f27ea8dda Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Thu, 23 May 2024 17:33:12 +0200 Subject: [PATCH 29/32] Renamed LegacyUtilities to GltfConverterUtility --- .../{LegacyUtilities.h => GltfConverterUtility.h} | 4 ++-- .../src/{LegacyUtilities.cpp => GltfConverterUtility.cpp} | 6 +++--- Cesium3DTilesContent/src/I3dmToGltfConverter.cpp | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) rename Cesium3DTilesContent/include/Cesium3DTilesContent/{LegacyUtilities.h => GltfConverterUtility.h} (98%) rename Cesium3DTilesContent/src/{LegacyUtilities.cpp => GltfConverterUtility.cpp} (96%) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverterUtility.h similarity index 98% rename from Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h rename to Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverterUtility.h index 15a12b041..3de33df47 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/LegacyUtilities.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverterUtility.h @@ -21,7 +21,7 @@ class Buffer; namespace Cesium3DTilesContent { -namespace LegacyUtilities { +namespace GltfConverterUtility { std::optional parseOffsetForSemantic( const rapidjson::Document& document, const char* semantic, @@ -120,5 +120,5 @@ toGlmQuat(const CesiumGltf::AccessorTypes::VEC4& gltfVal) { } } -} // namespace LegacyUtilities +} // namespace GltfConverterUtility } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/LegacyUtilities.cpp b/Cesium3DTilesContent/src/GltfConverterUtility.cpp similarity index 96% rename from Cesium3DTilesContent/src/LegacyUtilities.cpp rename to Cesium3DTilesContent/src/GltfConverterUtility.cpp index c49c6b55b..f2b180864 100644 --- a/Cesium3DTilesContent/src/LegacyUtilities.cpp +++ b/Cesium3DTilesContent/src/GltfConverterUtility.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include @@ -14,7 +14,7 @@ #include namespace Cesium3DTilesContent { -namespace LegacyUtilities { +namespace GltfConverterUtility { using namespace CesiumGltf; std::optional parseOffsetForSemantic( @@ -133,5 +133,5 @@ void applyRtcToNodes(Model& gltf, const glm::dvec3& rtc) { }); } -} // namespace LegacyUtilities +} // namespace GltfConverterUtility } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp index 8d55c3d03..196668794 100644 --- a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include #include @@ -25,7 +25,7 @@ using namespace CesiumGltf; namespace Cesium3DTilesContent { -using namespace LegacyUtilities; +using namespace GltfConverterUtility; namespace { struct I3dmHeader { From 2a284f5026a556784ba9c4928c8fb031b1b15ef8 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Thu, 23 May 2024 17:36:54 +0200 Subject: [PATCH 30/32] formatting and name change --- .../Cesium3DTilesContent/GltfConverterUtility.h | 3 ++- Cesium3DTilesContent/src/I3dmToGltfConverter.cpp | 2 +- .../include/CesiumNativeTests/RandomVector.h | 13 +++++-------- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverterUtility.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverterUtility.h index 3de33df47..1af55c82e 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverterUtility.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverterUtility.h @@ -32,7 +32,8 @@ typedef bool (rapidjson::Value::*ValuePredicate)() const; template bool isValue(const rapidjson::Value& value); template T getValue(const rapidjson::Value& value); -template std::optional getOpt(const rapidjson::Value& value) { +template +std::optional getOptional(const rapidjson::Value& value) { if (isValue(value)) { return std::make_optional(getValue(value)); } diff --git a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp index 196668794..66a81a067 100644 --- a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp @@ -1,8 +1,8 @@ // Heavily inspired by PntsToGltfConverter.cpp #include -#include #include +#include #include #include #include diff --git a/CesiumNativeTests/include/CesiumNativeTests/RandomVector.h b/CesiumNativeTests/include/CesiumNativeTests/RandomVector.h index 42bd5a0be..7cfc630ba 100644 --- a/CesiumNativeTests/include/CesiumNativeTests/RandomVector.h +++ b/CesiumNativeTests/include/CesiumNativeTests/RandomVector.h @@ -1,9 +1,9 @@ #pragma once #include +#include #include #include -#include #include #include @@ -12,16 +12,14 @@ namespace CesiumNativeTests { // Produce a random N-dimensional unit vector. Use a constant seed in order to // get a repeatable stream of vectors that can then be debugged! -template -struct RandomUnitVectorGenerator { +template struct RandomUnitVectorGenerator { using value_type = typename Vec::value_type; std::mt19937 gen; std::uniform_real_distribution dis; RandomUnitVectorGenerator() - : dis( - std::nextafter(static_cast(-1), value_type()), - static_cast(1)) { + : dis(std::nextafter(static_cast(-1), value_type()), + static_cast(1)) { gen.seed(42); } @@ -40,8 +38,7 @@ struct RandomUnitVectorGenerator { } }; -template -struct RandomQuaternionGenerator { +template struct RandomQuaternionGenerator { RandomUnitVectorGenerator> vecGenerator; glm::qua operator()() { From 80a50912a88ad6405962a7ae65d9e6d0a33c3547 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Fri, 24 May 2024 12:50:41 +0200 Subject: [PATCH 31/32] More changes in response to code review --- .../Cesium3DTilesContent/GltfConverters.h | 5 +- Cesium3DTilesContent/src/GltfConverters.cpp | 19 ++-- .../src/I3dmToGltfConverter.cpp | 94 ++++++++++--------- 3 files changed, 65 insertions(+), 53 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h index 651446e18..f988b8535 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h @@ -15,7 +15,7 @@ namespace Cesium3DTilesContent { -struct ByteResult { +struct AssetFetcherResult { std::vector bytes; CesiumUtility::ErrorList errorList; }; @@ -37,7 +37,8 @@ struct CESIUM3DTILESCONTENT_API AssetFetcher { tileTransform(tileTransform_), requestHeaders(requestHeaders_) {} - CesiumAsync::Future get(const std::string& relativeUrl) const; + CesiumAsync::Future + get(const std::string& relativeUrl) const; const CesiumAsync::AsyncSystem& asyncSystem; const std::shared_ptr pAssetAccessor; diff --git a/Cesium3DTilesContent/src/GltfConverters.cpp b/Cesium3DTilesContent/src/GltfConverters.cpp index 4872bc910..1940966ee 100644 --- a/Cesium3DTilesContent/src/GltfConverters.cpp +++ b/Cesium3DTilesContent/src/GltfConverters.cpp @@ -135,7 +135,7 @@ GltfConverters::ConverterFunction GltfConverters::getConverterByMagic( return nullptr; } -CesiumAsync::Future +CesiumAsync::Future AssetFetcher::get(const std::string& relativeUrl) const { auto resolvedUrl = Uri::resolve(baseUrl, relativeUrl); return pAssetAccessor->get(asyncSystem, resolvedUrl, requestHeaders) @@ -144,28 +144,31 @@ AssetFetcher::get(const std::string& relativeUrl) const { std::shared_ptr&& pCompletedRequest) { const CesiumAsync::IAssetResponse* pResponse = pCompletedRequest->response(); - ByteResult byteResult; + AssetFetcherResult assetFetcherResult; const auto& url = pCompletedRequest->url(); if (!pResponse) { - byteResult.errorList.emplaceError(fmt::format( + assetFetcherResult.errorList.emplaceError(fmt::format( "Did not receive a valid response for asset {}", url)); - return asyncSystem.createResolvedFuture(std::move(byteResult)); + return asyncSystem.createResolvedFuture( + std::move(assetFetcherResult)); } uint16_t statusCode = pResponse->statusCode(); if (statusCode != 0 && (statusCode < 200 || statusCode >= 300)) { - byteResult.errorList.emplaceError(fmt::format( + assetFetcherResult.errorList.emplaceError(fmt::format( "Received status code {} for asset {}", statusCode, url)); - return asyncSystem.createResolvedFuture(std::move(byteResult)); + return asyncSystem.createResolvedFuture( + std::move(assetFetcherResult)); } gsl::span asset = pResponse->data(); std::copy( asset.begin(), asset.end(), - std::back_inserter(byteResult.bytes)); - return asyncSystem.createResolvedFuture(std::move(byteResult)); + std::back_inserter(assetFetcherResult.bytes)); + return asyncSystem.createResolvedFuture( + std::move(assetFetcherResult)); }); } } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp index 66a81a067..add44895c 100644 --- a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp @@ -201,7 +201,7 @@ std::optional parseI3dmJson( } I3dmContent parsedContent; // Global semantics - if (auto optInstancesLength = + if (std::optional optInstancesLength = getValue(featureTableJson, "INSTANCES_LENGTH")) { parsedContent.instancesLength = *optInstancesLength; } else { @@ -231,7 +231,7 @@ std::optional parseI3dmJson( parseArrayValueDVec3(featureTableJson, "QUANTIZED_VOLUME_OFFSET"); if (!parsedContent.quantizedVolumeOffset.has_value()) { errors.emplaceError( - "Error parsing I3DM feature table, the I3dm uses quatized positions " + "Error parsing I3DM feature table, the I3dm uses quantized positions " "but has no valid QUANTIZED_VOLUME_OFFSET property"); return {}; } @@ -239,12 +239,13 @@ std::optional parseI3dmJson( parseArrayValueDVec3(featureTableJson, "QUANTIZED_VOLUME_SCALE"); if (!parsedContent.quantizedVolumeScale.has_value()) { errors.emplaceError( - "Error parsing I3DM feature table, the I3dm uses quatized positions " + "Error parsing I3DM feature table, the I3dm uses quantized positions " "but has no valid QUANTIZED_VOLUME_SCALE property"); return {}; } } - if (auto optENU = getValue(featureTableJson, "EAST_NORTH_UP")) { + if (std::optional optENU = + getValue(featureTableJson, "EAST_NORTH_UP")) { parsedContent.eastNorthUp = *optENU; } parsedContent.normalUp = @@ -301,10 +302,8 @@ CesiumAsync::Future convertI3dmContent( uint32_t headerLength, const CesiumGltfReader::GltfReaderOptions& options, const AssetFetcher& assetFetcher, - GltfConverterResult& result) { - ConvertedI3dm convertedI3dm; + ConvertedI3dm& convertedI3dm) { DecodedInstances& decodedInstances = convertedI3dm.decodedInstances; - convertedI3dm.gltfResult = result; auto finishEarly = [&]() { return assetFetcher.asyncSystem.createResolvedFuture( std::move(convertedI3dm)); @@ -319,7 +318,7 @@ CesiumAsync::Future convertI3dmContent( header.batchTableBinaryByteLength; const uint32_t gltfEnd = header.byteLength; auto gltfData = instancesBinary.subspan(gltfStart, gltfEnd - gltfStart); - std::optional> assetFuture; + std::optional> assetFuture; auto featureTableJsonData = instancesBinary.subspan(headerLength, header.featureTableJsonByteLength); std::optional parsedJsonResult = @@ -334,23 +333,23 @@ CesiumAsync::Future convertI3dmContent( auto featureTableBinaryData = instancesBinary.subspan( headerLength + header.featureTableJsonByteLength, header.featureTableBinaryByteLength); - auto binaryData = featureTableBinaryData.data(); + const std::byte* const pBinaryData = 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 rawPositions( reinterpret_cast( - binaryData + *parsedContent.position), + pBinaryData + *parsedContent.position), numInstances); decodedInstances.positions.assign(rawPositions.begin(), rawPositions.end()); } else { - gsl::span rawQPositions( + gsl::span rawQuantizedPositions( reinterpret_cast( - binaryData + *parsedContent.positionQuantized), + pBinaryData + *parsedContent.positionQuantized), numInstances); std::transform( - rawQPositions.begin(), - rawQPositions.end(), + rawQuantizedPositions.begin(), + rawQuantizedPositions.end(), decodedInstances.positions.begin(), [&parsedContent](auto&& posQuantized) { glm::vec3 position; @@ -370,11 +369,11 @@ CesiumAsync::Future convertI3dmContent( parsedContent.normalRight.has_value()) { gsl::span rawUp( reinterpret_cast( - binaryData + *parsedContent.normalUp), + pBinaryData + *parsedContent.normalUp), numInstances); gsl::span rawRight( reinterpret_cast( - binaryData + *parsedContent.normalRight), + pBinaryData + *parsedContent.normalRight), numInstances); std::transform( rawUp.begin(), @@ -389,11 +388,11 @@ CesiumAsync::Future convertI3dmContent( gsl::span rawUpOct( reinterpret_cast( - binaryData + *parsedContent.normalUpOct32p), + pBinaryData + *parsedContent.normalUpOct32p), numInstances); gsl::span rawRightOct( reinterpret_cast( - binaryData + *parsedContent.normalRightOct32p), + pBinaryData + *parsedContent.normalRightOct32p), numInstances); std::transform( rawUpOct.begin(), @@ -411,23 +410,28 @@ CesiumAsync::Future convertI3dmContent( worldTransform = translate(worldTransform, *decodedInstances.rtcCenter); } auto worldTransformInv = inverse(worldTransform); - for (size_t i = 0; i < decodedInstances.positions.size(); ++i) { - auto worldPos = - worldTransform * glm::dvec4(decodedInstances.positions[i], 1.0); - CesiumGeospatial::LocalHorizontalCoordinateSystem enu( - (glm::dvec3(worldPos))); - const auto& ecef = enu.getLocalToEcefTransformation(); - // back into tile coordinate system - auto tileFrame = worldTransformInv * ecef; - glm::quat tileFrameRot = - rotationFromUpRight(glm::vec3(tileFrame[1]), glm::vec3(tileFrame[0])); - decodedInstances.rotations[i] = tileFrameRot; - } + std::transform( + decodedInstances.positions.begin(), + decodedInstances.positions.end(), + decodedInstances.rotations.begin(), + [&](const glm::vec3& position) { + // Find the ENU transform using global coordinates. + glm::dvec4 worldPos = worldTransform * glm::dvec4(position, 1.0); + CesiumGeospatial::LocalHorizontalCoordinateSystem enu( + (glm::dvec3(worldPos))); + const glm::dmat4& ecef = enu.getLocalToEcefTransformation(); + // Express the rotation in the tile's coordinate system, like explicit + // I3dm instance rotations. + glm::dmat4 tileFrame = worldTransformInv * ecef; + return rotationFromUpRight( + glm::vec3(tileFrame[1]), + glm::vec3(tileFrame[0])); + }); } decodedInstances.scales.resize(numInstances, glm::vec3(1.0, 1.0, 1.0)); if (parsedContent.scale.has_value()) { gsl::span rawScales( - reinterpret_cast(binaryData + *parsedContent.scale), + reinterpret_cast(pBinaryData + *parsedContent.scale), numInstances); std::transform( rawScales.begin(), @@ -438,7 +442,7 @@ CesiumAsync::Future convertI3dmContent( if (parsedContent.scaleNonUniform.has_value()) { gsl::span rawScalesNonUniform( reinterpret_cast( - binaryData + *parsedContent.scaleNonUniform), + pBinaryData + *parsedContent.scaleNonUniform), numInstances); std::transform( decodedInstances.scales.begin(), @@ -450,7 +454,7 @@ CesiumAsync::Future convertI3dmContent( }); } repositionInstances(decodedInstances); - ByteResult byteResult; + AssetFetcherResult assetFetcherResult; if (header.gltfFormat == 0) { // Recursively fetch and read the glTF content. auto gltfUri = std::string( @@ -458,16 +462,16 @@ CesiumAsync::Future convertI3dmContent( gltfData.size()); return assetFetcher.get(gltfUri) .thenImmediately( - [options, assetFetcher](ByteResult&& byteResult) + [options, assetFetcher](AssetFetcherResult&& assetFetcherResult) -> CesiumAsync::Future { - if (byteResult.errorList.hasErrors()) { + if (assetFetcherResult.errorList.hasErrors()) { GltfConverterResult errorResult; - errorResult.errors.merge(byteResult.errorList); + errorResult.errors.merge(assetFetcherResult.errorList); return assetFetcher.asyncSystem.createResolvedFuture( std::move(errorResult)); } return BinaryToGltfConverter::convert( - byteResult.bytes, + assetFetcherResult.bytes, options, assetFetcher); }) @@ -749,21 +753,25 @@ CesiumAsync::Future I3dmToGltfConverter::convert( const gsl::span& instancesBinary, const CesiumGltfReader::GltfReaderOptions& options, const AssetFetcher& assetFetcher) { - GltfConverterResult result; + ConvertedI3dm convertedI3dm; I3dmHeader header; uint32_t headerLength = 0; - parseI3dmHeader(instancesBinary, header, headerLength, result); - if (result.errors) { - return assetFetcher.asyncSystem.createResolvedFuture(std::move(result)); + parseI3dmHeader( + instancesBinary, + header, + headerLength, + convertedI3dm.gltfResult); + if (convertedI3dm.gltfResult.errors) { + return assetFetcher.asyncSystem.createResolvedFuture( + std::move(convertedI3dm.gltfResult)); } - DecodedInstances decodedInstances; return convertI3dmContent( instancesBinary, header, headerLength, options, assetFetcher, - result) + convertedI3dm) .thenImmediately([](ConvertedI3dm&& convertedI3dm) { if (convertedI3dm.gltfResult.errors.hasErrors()) { return convertedI3dm.gltfResult; From ba68601bac60fb807764cde8520f3295422cee16 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Fri, 24 May 2024 13:39:10 +0200 Subject: [PATCH 32/32] Renaming and simplification --- .../GltfConverterUtility.h | 7 +++ .../src/I3dmToGltfConverter.cpp | 62 +++++++++++-------- 2 files changed, 42 insertions(+), 27 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverterUtility.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverterUtility.h index 1af55c82e..2ce646980 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverterUtility.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverterUtility.h @@ -93,6 +93,13 @@ int32_t createAccessorInGltf( const int64_t count, const std::string type); +/** + * Applies the given relative-to-center (RTC) translation to the transforms of + * all nodes in the glTF. This is useful in converting i3dm files, where the RTC + * translation must be applied to the model before the i3dm instance + * transform. It's also the 3D Tiles 1.1 "way" to do away with RTC and encode it + * directly in the glTF. + */ void applyRtcToNodes(CesiumGltf::Model& gltf, const glm::dvec3& rtc); template diff --git a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp index add44895c..2ccab8837 100644 --- a/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/I3dmToGltfConverter.cpp @@ -608,7 +608,7 @@ const size_t scaleOffset = rotOffset + sizeof(glm::quat); const size_t totalStride = sizeof(glm::vec3) + sizeof(glm::quat) + sizeof(glm::vec3); -void copyToBuffer( +void copyInstanceToBuffer( const glm::dvec3& position, const glm::dquat& rotation, const glm::dvec3& scale, @@ -621,13 +621,24 @@ void copyToBuffer( std::memcpy(bufferLoc + scaleOffset, &fscale, sizeof(fscale)); } -void copyToBuffer( +void copyInstanceToBuffer( const glm::dvec3& position, const glm::dquat& rotation, const glm::dvec3& scale, std::vector& bufferData, size_t i) { - copyToBuffer(position, rotation, scale, &bufferData[i * totalStride]); + copyInstanceToBuffer(position, rotation, scale, &bufferData[i * totalStride]); +} + +void copyInstanceToBuffer( + const glm::dmat4& instanceTransform, + std::vector& bufferData, + size_t i) { + glm::dvec3 position, scale, skew; + glm::dquat rotation; + glm::dvec4 perspective; + decompose(instanceTransform, scale, rotation, position, skew, perspective); + copyInstanceToBuffer(position, rotation, scale, bufferData, i); } void instantiateGltfInstances( @@ -656,54 +667,50 @@ void instantiateGltfInstances( Mesh&, MeshPrimitive&, const glm::dmat4& transform) { - auto [nodeItr, notSeen] = meshNodes.insert(&node); - if (!notSeen) { + auto [nodeItr, inserted] = meshNodes.insert(&node); + if (!inserted) { return; } - std::vector existingInstances{glm::dmat4(1.0)}; + std::vector modelInstanceTransforms{glm::dmat4(1.0)}; auto& gpuExt = node.addExtension(); 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 = getMeshGpuInstancingTransforms( + modelInstanceTransforms = getMeshGpuInstancingTransforms( *result.model, gpuExt, result.errors); - if (numInstances * existingInstances.size() > + if (numInstances * modelInstanceTransforms.size() > std::numeric_limits::max()) { result.errors.emplaceError(fmt::format( "Too many instances: {} from i3dm and {} from glb", numInstances, - existingInstances.size())); + modelInstanceTransforms.size())); return; } } if (result.errors.hasErrors()) { return; } - const uint32_t numNewInstances = - static_cast(numInstances * existingInstances.size()); + const uint32_t numNewInstances = static_cast( + numInstances * modelInstanceTransforms.size()); const size_t instanceDataSize = totalStride * numNewInstances; auto dataBaseOffset = static_cast(instanceBuffer.cesium.data.size()); instanceBuffer.cesium.data.resize(dataBaseOffset + instanceDataSize); // Transform instance transform into local glTF coordinate system. - const auto toTile = upToZ * transform; - const auto toTileInv = inverse(toTile); + const glm::dmat4 toTile = upToZ * transform; + const glm::dmat4 toTileInv = inverse(toTile); size_t destInstanceIndx = 0; for (unsigned i = 0; i < numInstances; ++i) { - auto instMat = composeInstanceTransform(i, decodedInstances); - instMat = toTileInv * instMat * toTile; - for (const auto& innerMat : existingInstances) { - auto finalMat = instMat * innerMat; - glm::dvec3 position, scale, skew; - glm::dquat rotation; - glm::dvec4 perspective; - decompose(finalMat, scale, rotation, position, skew, perspective); - copyToBuffer( - position, - rotation, - scale, + const glm::dmat4 instanceTransform = + toTileInv * composeInstanceTransform(i, decodedInstances) * + toTile; + for (const auto& modelInstanceTransform : modelInstanceTransforms) { + glm::dmat4 finalTransform = + instanceTransform * modelInstanceTransform; + copyInstanceToBuffer( + finalTransform, instanceBuffer.cesium.data, destInstanceIndx++); } @@ -714,8 +721,9 @@ void instantiateGltfInstances( Accessor::ComponentType::FLOAT, numNewInstances, Accessor::Type::VEC3); - auto& posAcessor = gltf.accessors[static_cast(posAccessorId)]; - posAcessor.byteOffset = dataBaseOffset; + auto& posAccessor = + gltf.accessors[static_cast(posAccessorId)]; + posAccessor.byteOffset = dataBaseOffset; gpuExt.attributes["TRANSLATION"] = posAccessorId; auto rotAccessorId = createAccessorInGltf( gltf,