diff --git a/.gitmodules b/.gitmodules index c5734baec..f4b8b8a7a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "extern/tinygltf"] - path = extern/tinygltf - url = https://github.com/syoyo/tinygltf [submodule "extern/glm"] path = extern/glm url = https://github.com/g-truc/glm.git @@ -28,3 +25,9 @@ [submodule "extern/rapidjson"] path = extern/rapidjson url = https://github.com/Tencent/rapidjson.git +[submodule "extern/glTF"] + path = extern/glTF + url = https://github.com/KhronosGroup/glTF.git +[submodule "extern/stb"] + path = extern/stb + url = https://github.com/nothings/stb.git diff --git a/.vscode/settings.json b/.vscode/settings.json index d19550f28..600e29f74 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -95,7 +95,11 @@ "shared_mutex": "cpp", "gsl_assert": "cpp", "compare": "cpp", - "resumable": "cpp" + "resumable": "cpp", + "codecvt": "cpp", + "ranges": "cpp", + "scoped_allocator": "cpp", + "typeindex": "cpp" }, "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools", "cmake.configureOnOpen": true diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e1188e94..513fe8619 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,6 +80,8 @@ target_link_libraries(cesium-native-tests PUBLIC Catch2::Catch2) add_subdirectory(CesiumUtility) add_subdirectory(CesiumGeometry) add_subdirectory(CesiumGeospatial) +add_subdirectory(CesiumGltf) +add_subdirectory(CesiumGltfReader) add_subdirectory(CesiumAsync) add_subdirectory(Cesium3DTiles) diff --git a/Cesium3DTiles/CMakeLists.txt b/Cesium3DTiles/CMakeLists.txt index 58bc4776a..8f8ff568d 100644 --- a/Cesium3DTiles/CMakeLists.txt +++ b/Cesium3DTiles/CMakeLists.txt @@ -16,14 +16,11 @@ target_sources( target_include_directories( Cesium3DTiles SYSTEM PUBLIC - # We're not using CMake for tinygltf at all, but it's header only. Add its headers here. - ${CMAKE_CURRENT_SOURCE_DIR}/../extern/tinygltf - - # Same with RapidJSON ${CMAKE_CURRENT_SOURCE_DIR}/../extern/rapidjson/include # Draco's CMake doesn't use target_include_directories, so we need to manually add draco's include dirs here. ${CMAKE_CURRENT_SOURCE_DIR}/../extern/draco/src + ${CMAKE_BINARY_DIR} PUBLIC include @@ -33,4 +30,4 @@ target_include_directories( "$>:${CMAKE_CURRENT_SOURCE_DIR}/src>>" ) -target_link_libraries(Cesium3DTiles PUBLIC CesiumAsync CesiumGeospatial CesiumGeometry CesiumUtility uriparser draco tinyxml2 spdlog) +target_link_libraries(Cesium3DTiles PUBLIC CesiumAsync CesiumGeospatial CesiumGeometry CesiumGltf CesiumGltfReader CesiumUtility uriparser draco tinyxml2 spdlog) diff --git a/Cesium3DTiles/include/Cesium3DTiles/Gltf.h b/Cesium3DTiles/include/Cesium3DTiles/Gltf.h index 30d3975aa..e68dd8b99 100644 --- a/Cesium3DTiles/include/Cesium3DTiles/Gltf.h +++ b/Cesium3DTiles/include/Cesium3DTiles/Gltf.h @@ -1,17 +1,18 @@ #pragma once #include "Cesium3DTiles/Library.h" +#include "CesiumGltf/Model.h" #include #include #include -#include +#include namespace Cesium3DTiles { /** * @brief Functions for loading and processing glTF models. * - * This class offers basic functions for loading glTF models as `tinygltf::Model` + * This class offers basic functions for loading glTF models as `CesiumGltf::Model` * instances, and for processing the mesh primitives that appear in the resulting * models. */ @@ -22,45 +23,14 @@ namespace Cesium3DTiles { /** @brief This class cannot be instantiated */ Gltf() = delete; - /** - * @brief A summary of the result of loading a glTF model from raw data. - */ - struct LoadResult { - /** - * @brief The gltf model that was loaded. - * - * This will be `std::nullopt` if the model was not loaded successfully. - */ - std::optional model; - - /** - * @brief Warning messages from the attempt to load the model. - */ - std::string warnings; - - /** - * @brief Error messages from the attempt to load the model. - */ - std::string errors; - }; - - /** - * @brief Load a glTF model from the given input data. - * - * @param data The input data. - * @return The {@link LoadResult} containing the model (if it could be loaded), - * and possible warning- or error messages. - */ - static LoadResult load(const gsl::span& data); - /** * @brief A callback function for {@link Gltf::forEachPrimitiveInScene}. */ typedef void ForEachPrimitiveInSceneCallback( - tinygltf::Model& gltf, - tinygltf::Node& node, - tinygltf::Mesh& mesh, - tinygltf::Primitive& primitive, + CesiumGltf::Model& gltf, + CesiumGltf::Node& node, + CesiumGltf::Mesh& mesh, + CesiumGltf::MeshPrimitive& primitive, const glm::dmat4& transform ); @@ -86,21 +56,21 @@ namespace Cesium3DTiles { * @param sceneID The scene ID (index) * @param callback The callback to apply */ - static void forEachPrimitiveInScene(tinygltf::Model& gltf, int sceneID, std::function&& callback); + static void forEachPrimitiveInScene(CesiumGltf::Model& gltf, int sceneID, std::function&& callback); /** * @brief A callback function for {@link Gltf::forEachPrimitiveInScene}. */ typedef void ForEachPrimitiveInSceneConstCallback( - const tinygltf::Model& gltf, - const tinygltf::Node& node, - const tinygltf::Mesh& mesh, - const tinygltf::Primitive& primitive, + const CesiumGltf::Model& gltf, + const CesiumGltf::Node& node, + const CesiumGltf::Mesh& mesh, + const CesiumGltf::MeshPrimitive& primitive, const glm::dmat4& transform ); /** @copydoc Gltf::forEachPrimitiveInScene() */ - static void forEachPrimitiveInScene(const tinygltf::Model& gltf, int sceneID, std::function&& callback); + static void forEachPrimitiveInScene(const CesiumGltf::Model& gltf, int sceneID, std::function&& callback); }; diff --git a/Cesium3DTiles/include/Cesium3DTiles/GltfAccessor.h b/Cesium3DTiles/include/Cesium3DTiles/GltfAccessor.h deleted file mode 100644 index cc1a87706..000000000 --- a/Cesium3DTiles/include/Cesium3DTiles/GltfAccessor.h +++ /dev/null @@ -1,154 +0,0 @@ -#pragma once - -#include "Gltf.h" -#include - -namespace Cesium3DTiles { - - /** - * @brief A view on the data of one accessor of a glTF asset. - * - * It provides the actual accessor data like an array of elements. - * The type of the accessor elements is determined by the template - * parameter. Instances are created from an input glTF model - * and an accessor index, and the {@link operator[]()} - * can be used to access the elements: - * - * ``` - * tinygltf::Model model; - * GltfAccessor positions(model, accessorIndex) - * glm::vec3 position = positions[i]; - * ``` - * - * @tparam T The type of the elements in the accessor. - */ - template - class GltfAccessor final { - private: - const tinygltf::Buffer* _pGltfBuffer; - const tinygltf::BufferView* _pGltfBufferView; - const tinygltf::Accessor* _pGltfAccessor; - const unsigned char* _pBufferViewData; - size_t _stride; - size_t _offset; - size_t _size; - - public: - typedef T value_type; - - /** - * @brief Creates a new instance. - * - * The resulting instance will provide the data of the specified - * accessor from the given model. - * - * @param model The glTF model. - * @param accessorID The ID (index) of the accessor. - * @throws An `std::runtime_error` may be thrown when there are - * inconsistencies in the given model. This may refer to the model - * itself, or to cases where the size of the template parameter `T` - * does not match the size of the elements of the specified accessor. - */ - GltfAccessor(const tinygltf::Model& model, size_t accessorID) - { - const tinygltf::Accessor& accessor = model.accessors[accessorID]; - const tinygltf::BufferView& bufferView = model.bufferViews[accessor.bufferView]; - const tinygltf::Buffer& buffer = model.buffers[bufferView.buffer]; - - const std::vector& data = buffer.data; - size_t bufferBytes = data.size(); - if (bufferView.byteOffset + bufferView.byteLength > bufferBytes) - { - throw std::runtime_error("bufferView does not fit in buffer."); - } - - int accessorByteStride = accessor.ByteStride(bufferView); - if (accessorByteStride == -1) - { - throw std::runtime_error("cannot compute accessor byteStride."); - } - - int32_t accessorComponentElements = tinygltf::GetNumComponentsInType(accessor.type); - int32_t accessorComponentBytes = tinygltf::GetComponentSizeInBytes(accessor.componentType); - int32_t accessorBytesPerStride = accessorComponentElements * accessorComponentBytes; - - if (sizeof(T) != accessorBytesPerStride) - { - throw std::runtime_error("sizeof(T) does not much accessor bytes."); - } - - size_t accessorBytes = accessorByteStride * accessor.count; - size_t bytesRemainingInBufferView = bufferView.byteLength - (accessor.byteOffset + accessorByteStride * (accessor.count - 1) + accessorBytesPerStride); - if (accessorBytes > bufferView.byteLength || bytesRemainingInBufferView < 0) - { - throw std::runtime_error("accessor does not fit in bufferView."); - } - - const unsigned char* pBufferData = &data[0]; - const unsigned char* pBufferViewData = pBufferData + bufferView.byteOffset; - - this->_pGltfBuffer = &buffer; - this->_pGltfBufferView = &bufferView; - this->_pGltfAccessor = &accessor; - this->_stride = accessorByteStride; - this->_offset = accessor.byteOffset; - this->_size = accessor.count; - this->_pBufferViewData = pBufferViewData; - } - - /** - * @brief Provides the specified accessor element. - * - * @param i The index of the element. - * @returns The constant reference to the accessor element. - * @throws A `std::range_error` if the given index is negative - * or not smaller than the {@link size} of this accessor. - */ - const T& operator[](size_t i) const - { - if (i < 0 || i >= this->_size) - { - throw std::range_error("index out of range"); - } - - return *reinterpret_cast(this->_pBufferViewData + i * this->_stride + this->_offset); - } - - /** - * @brief Returns the size (number of elements) of this accessor. - * - * This is the number of elements of type `T` that this accessor contains. - * - * @returns The size. - */ - size_t size() const noexcept - { - return this->_size; - } - - /** - * @brief Returns the underyling buffer implementation. - */ - const tinygltf::Buffer& gltfBuffer() const - { - return *this->_pGltfBuffer; - } - - /** - * @brief Returns the underyling buffer view implementation. - */ - const tinygltf::BufferView& gltfBufferView() const noexcept - { - return *this->_pGltfBufferView; - } - - /** - * @brief Returns the underyling acessor implementation. - */ - const tinygltf::Accessor& gltfAccessor() const noexcept - { - return *this->_pGltfAccessor; - } - }; - -} \ No newline at end of file diff --git a/Cesium3DTiles/include/Cesium3DTiles/GltfContent.h b/Cesium3DTiles/include/Cesium3DTiles/GltfContent.h index 8cb13c681..7ce38679b 100644 --- a/Cesium3DTiles/include/Cesium3DTiles/GltfContent.h +++ b/Cesium3DTiles/include/Cesium3DTiles/GltfContent.h @@ -50,7 +50,7 @@ namespace Cesium3DTiles { * @return The bounding region. */ static CesiumGeospatial::BoundingRegion createRasterOverlayTextureCoordinates( - tinygltf::Model& gltf, + CesiumGltf::Model& gltf, uint32_t textureCoordinateID, const CesiumGeospatial::Projection& projection, const CesiumGeometry::Rectangle& rectangle diff --git a/Cesium3DTiles/include/Cesium3DTiles/GltfWriter.h b/Cesium3DTiles/include/Cesium3DTiles/GltfWriter.h deleted file mode 100644 index 3527d7c14..000000000 --- a/Cesium3DTiles/include/Cesium3DTiles/GltfWriter.h +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once - -#include "Gltf.h" -#include "GltfAccessor.h" - -#include - -namespace Cesium3DTiles { - - /** - * @brief Provides write access to a {@link GltfAccessor}. - */ - template - class GltfWriter final { - private: - GltfAccessor _accessor; - - public: - - /** @copydoc GltfAccessor::GltfAccessor */ - GltfWriter(tinygltf::Model& model, size_t accessorID) : - _accessor(model, accessorID) - { - } - - /** @copydoc GltfAccessor::operator[]() */ - const T& operator[](size_t i) const { - return this->_accessor[i]; - } - - /** @copydoc GltfAccessor::operator[]() */ - T& operator[](size_t i) { - return const_cast(this->_accessor[i]); - } - - /** @copydoc GltfAccessor::size */ - size_t size() const noexcept { - return this->_accessor.size(); - } - - /** @copydoc GltfAccessor::gltfBuffer */ - const tinygltf::Buffer& gltfBuffer() const noexcept - { - return this->_accessor.gltfBuffer(); - } - - /** @copydoc GltfAccessor::gltfBuffer */ - tinygltf::Buffer& gltfBuffer() noexcept - { - return const_cast(this->_accessor.gltfBuffer()); - } - - /** @copydoc GltfAccessor::gltfBufferView */ - const tinygltf::BufferView& gltfBufferView() const noexcept - { - return this->_accessor.gltfBufferView(); - } - - /** @copydoc GltfAccessor::gltfBufferView */ - tinygltf::BufferView& gltfBufferView() noexcept { - return const_cast(this->_accessor.gltfBufferView()); - } - - /** @copydoc GltfAccessor::gltfAccessor */ - const tinygltf::Accessor& gltfAccessor() const noexcept { - return this->_accessor.gltfAccessor(); - } - - /** @copydoc GltfAccessor::gltfAccessor */ - tinygltf::Accessor& gltfAccessor() noexcept { - return const_cast(this->_accessor.gltfAccessor()); - } - }; - -} \ No newline at end of file diff --git a/Cesium3DTiles/include/Cesium3DTiles/IPrepareRendererResources.h b/Cesium3DTiles/include/Cesium3DTiles/IPrepareRendererResources.h index a949f80a7..669064d33 100644 --- a/Cesium3DTiles/include/Cesium3DTiles/IPrepareRendererResources.h +++ b/Cesium3DTiles/include/Cesium3DTiles/IPrepareRendererResources.h @@ -9,9 +9,9 @@ namespace CesiumGeometry { struct Rectangle; } -namespace tinygltf { - struct Image; - class Model; +namespace CesiumGltf { + struct ImageCesium; + struct Model; } namespace Cesium3DTiles { @@ -44,7 +44,7 @@ namespace Cesium3DTiles { * @returns Arbitrary data representing the result of the load process. This data is * passed to {@link prepareInMainThread} as the `pLoadThreadResult` parameter. */ - virtual void* prepareInLoadThread(const tinygltf::Model& model, const glm::dmat4& transform) = 0; + virtual void* prepareInLoadThread(const CesiumGltf::Model& model, const glm::dmat4& transform) = 0; /** * @brief Further prepares renderer resources. @@ -85,7 +85,7 @@ namespace Cesium3DTiles { * @returns Arbitrary data representing the result of the load process. This data is * passed to {@link prepareRasterInMainThread} as the `pLoadThreadResult` parameter. */ - virtual void* prepareRasterInLoadThread(const tinygltf::Image& image) = 0; + virtual void* prepareRasterInLoadThread(const CesiumGltf::ImageCesium& image) = 0; /** * @brief Further preprares a raster overlay tile. diff --git a/Cesium3DTiles/include/Cesium3DTiles/RasterOverlay.h b/Cesium3DTiles/include/Cesium3DTiles/RasterOverlay.h index b9a6d525c..8fffb1fe4 100644 --- a/Cesium3DTiles/include/Cesium3DTiles/RasterOverlay.h +++ b/Cesium3DTiles/include/Cesium3DTiles/RasterOverlay.h @@ -76,6 +76,7 @@ namespace Cesium3DTiles { * @param asyncSystem The async system used to request assets and do work in threads. * @param pCreditSystem The {@link CreditSystem} to use when creating a per-TileProvider {@link Credit}. * @param pPrepareRendererResources The interface used to prepare raster images for rendering. + * @param pLogger The logger to which to send messages about the tile provider and tiles. */ void createTileProvider( const CesiumAsync::AsyncSystem& asyncSystem, @@ -93,6 +94,7 @@ namespace Cesium3DTiles { * @param asyncSystem The async system used to request assets and do work in threads. * @param pCreditSystem The {@link CreditSystem} to use when creating a per-TileProvider {@link Credit}. * @param pPrepareRendererResources The interface used to prepare raster images for rendering. + * @param pLogger The logger to which to send messages about the tile provider and tiles. * @param pOwner The overlay that owns this overlay, or nullptr if this overlay is not aggregated. * @return The future that contains the tile provider when it is ready, or the `nullptr` in case * of an error. diff --git a/Cesium3DTiles/include/Cesium3DTiles/RasterOverlayTile.h b/Cesium3DTiles/include/Cesium3DTiles/RasterOverlayTile.h index 2dbe163ed..25c75304c 100644 --- a/Cesium3DTiles/include/Cesium3DTiles/RasterOverlayTile.h +++ b/Cesium3DTiles/include/Cesium3DTiles/RasterOverlayTile.h @@ -127,7 +127,7 @@ namespace Cesium3DTiles { * * @return The image data. */ - const tinygltf::Image& getImage() const noexcept { return this->_image; } + const CesiumGltf::ImageCesium& getImage() const noexcept { return this->_image; } /** * @brief Create the renderer resources for the loaded image. @@ -170,7 +170,7 @@ namespace Cesium3DTiles { CesiumGeometry::QuadtreeTileID _tileID; std::vector _tileCredits; std::atomic _state; - tinygltf::Image _image; + CesiumGltf::ImageCesium _image; void* _pRendererResources; uint32_t _references; }; diff --git a/Cesium3DTiles/include/Cesium3DTiles/RasterOverlayTileProvider.h b/Cesium3DTiles/include/Cesium3DTiles/RasterOverlayTileProvider.h index c56bc3231..66d603ed5 100644 --- a/Cesium3DTiles/include/Cesium3DTiles/RasterOverlayTileProvider.h +++ b/Cesium3DTiles/include/Cesium3DTiles/RasterOverlayTileProvider.h @@ -10,6 +10,7 @@ #include "CesiumUtility/IntrusivePointer.h" #include #include +#include namespace Cesium3DTiles { @@ -41,6 +42,7 @@ namespace Cesium3DTiles { * @param asyncSystem The async system used to request assets and do work in threads. * @param credit The {@link Credit} for this tile provider, if it exists. * @param pPrepareRendererResources The interface used to prepare raster images for rendering. + * @param pLogger The logger to which to send messages about the tile provider and tiles. * @param projection The {@link CesiumGeospatial::Projection}. * @param tilingScheme The {@link CesiumGeometry::QuadtreeTilingScheme}. * @param coverageRectangle The {@link CesiumGeometry::Rectangle}. @@ -54,6 +56,7 @@ namespace Cesium3DTiles { const CesiumAsync::AsyncSystem& asyncSystem, std::optional credit, std::shared_ptr pPrepareRendererResources, + std::shared_ptr pLogger, const CesiumGeospatial::Projection& projection, const CesiumGeometry::QuadtreeTilingScheme& tilingScheme, const CesiumGeometry::Rectangle& coverageRectangle, @@ -92,6 +95,11 @@ namespace Cesium3DTiles { */ const std::shared_ptr& getPrepareRendererResources() const noexcept { return this->_pPrepareRendererResources; } + /** + * @brief Gets the logger to which to send messages about the tile provider and tiles. + */ + const std::shared_ptr& getLogger() const noexcept { return this->_pLogger; } + /** * @brief Returns the {@link CesiumGeospatial::Projection} of this instance. */ @@ -189,7 +197,7 @@ namespace Cesium3DTiles { /** * @brief Gets the number of bytes of tile data that are currently loaded. */ - size_t getTileDataBytes() const noexcept { return this->_tileDataBytes; } + int64_t getTileDataBytes() const noexcept { return this->_tileDataBytes; } /** * @brief Returns the number of tiles that are currently loading. @@ -231,6 +239,7 @@ namespace Cesium3DTiles { CesiumAsync::AsyncSystem _asyncSystem; std::optional _credit; std::shared_ptr _pPrepareRendererResources; + std::shared_ptr _pLogger; CesiumGeospatial::Projection _projection; CesiumGeometry::QuadtreeTilingScheme _tilingScheme; CesiumGeometry::Rectangle _coverageRectangle; @@ -240,7 +249,7 @@ namespace Cesium3DTiles { uint32_t _imageHeight; std::unordered_map> _tiles; std::unique_ptr _pPlaceholder; - size_t _tileDataBytes; + int64_t _tileDataBytes; uint32_t _tilesCurrentlyLoading; }; } diff --git a/Cesium3DTiles/include/Cesium3DTiles/Tile.h b/Cesium3DTiles/include/Cesium3DTiles/Tile.h index 3c5c60c2d..d79cc469e 100644 --- a/Cesium3DTiles/include/Cesium3DTiles/Tile.h +++ b/Cesium3DTiles/include/Cesium3DTiles/Tile.h @@ -467,7 +467,7 @@ namespace Cesium3DTiles { /** * @brief Determines the number of bytes in this tile's geometry and texture data. */ - size_t computeByteSize() const noexcept; + int64_t computeByteSize() const noexcept; private: @@ -485,7 +485,7 @@ namespace Cesium3DTiles { * * @return The bounding region */ - static std::optional generateTextureCoordinates(tinygltf::Model& model, const BoundingVolume& boundingVolume, const std::vector& projections); + static std::optional generateTextureCoordinates(CesiumGltf::Model& model, const BoundingVolume& boundingVolume, const std::vector& projections); /** * @brief Upsample the parent of this tile. diff --git a/Cesium3DTiles/include/Cesium3DTiles/TileContentLoadResult.h b/Cesium3DTiles/include/Cesium3DTiles/TileContentLoadResult.h index 6626ba3bb..a78637d87 100644 --- a/Cesium3DTiles/include/Cesium3DTiles/TileContentLoadResult.h +++ b/Cesium3DTiles/include/Cesium3DTiles/TileContentLoadResult.h @@ -1,6 +1,5 @@ #pragma once -#include "Cesium3DTiles/Gltf.h" #include "Cesium3DTiles/Tile.h" #include "Cesium3DTiles/TileContext.h" #include "CesiumGeometry/QuadtreeTileRectangularRange.h" @@ -30,7 +29,7 @@ namespace Cesium3DTiles { * If it has a value but the model is blank, the tile can * be "rendered", but it is rendered as nothing. */ - std::optional model; + std::optional model; /** * @brief A new context, if any, used by the `childTiles`. diff --git a/Cesium3DTiles/include/Cesium3DTiles/Tileset.h b/Cesium3DTiles/include/Cesium3DTiles/Tileset.h index ccdbb1a49..4b108a539 100644 --- a/Cesium3DTiles/include/Cesium3DTiles/Tileset.h +++ b/Cesium3DTiles/include/Cesium3DTiles/Tileset.h @@ -126,7 +126,7 @@ namespace Cesium3DTiles { * loaded bytes is greater than this value, tiles will be unloaded until the total is under * this number or until only required tiles remain, whichever comes first. */ - size_t maximumCachedBytes = 512 * 1024 * 1024; + int64_t maximumCachedBytes = 512 * 1024 * 1024; /** * @brief A table that maps the camera height above the ellipsoid to a fog density. Tiles that are in full fog are culled. @@ -326,7 +326,7 @@ namespace Cesium3DTiles { /** * @brief Gets the total number of bytes of tile and raster overlay data that are currently loaded. */ - size_t getTotalDataBytes() const noexcept; + int64_t getTotalDataBytes() const noexcept; private: struct TraversalDetails { @@ -527,7 +527,7 @@ namespace Cesium3DTiles { RasterOverlayCollection _overlays; - size_t _tileDataBytes; + int64_t _tileDataBytes; static void addTileToLoadQueue(std::vector& loadQueue, const FrameState& frameState, Tile& tile, double distance); static void processQueue(std::vector& queue, std::atomic& loadsInProgress, uint32_t maximumLoadsInProgress); diff --git a/Cesium3DTiles/src/Batched3DModelContent.cpp b/Cesium3DTiles/src/Batched3DModelContent.cpp index b026f3699..2ff473db2 100644 --- a/Cesium3DTiles/src/Batched3DModelContent.cpp +++ b/Cesium3DTiles/src/Batched3DModelContent.cpp @@ -40,7 +40,7 @@ namespace Cesium3DTiles { void parseFeatureTableJsonData( const std::shared_ptr& pLogger, - tinygltf::Model& gltf, + CesiumGltf::Model& gltf, const gsl::span& featureTableJsonData) { rapidjson::Document document; @@ -60,19 +60,12 @@ namespace Cesium3DTiles { rtcIt->value[2].IsDouble() ) { // Add the RTC_CENTER value to the glTF itself. - tinygltf::Value::Object extras; - if (gltf.extras.IsObject()) { - extras = gltf.extras.Get(); - } - rapidjson::Value& rtcValue = rtcIt->value; - extras["RTC_CENTER"] = tinygltf::Value(tinygltf::Value::Array{ - tinygltf::Value(rtcValue[0].GetDouble()), - tinygltf::Value(rtcValue[1].GetDouble()), - tinygltf::Value(rtcValue[2].GetDouble()) - }); - - gltf.extras = tinygltf::Value(extras); + gltf.extras["RTC_CENTER"] = { + rtcValue[0].GetDouble(), + rtcValue[1].GetDouble(), + rtcValue[2].GetDouble() + }; } } @@ -171,7 +164,7 @@ namespace Cesium3DTiles { ); if (pResult->model && header.featureTableJsonByteLength > 0) { - tinygltf::Model& gltf = pResult->model.value(); + CesiumGltf::Model& gltf = pResult->model.value(); gsl::span featureTableJsonData = data.subspan(headerLength, header.featureTableJsonByteLength); parseFeatureTableJsonData(pLogger, gltf, featureTableJsonData); } diff --git a/Cesium3DTiles/src/BingMapsRasterOverlay.cpp b/Cesium3DTiles/src/BingMapsRasterOverlay.cpp index 2c4431df4..da0fa691d 100644 --- a/Cesium3DTiles/src/BingMapsRasterOverlay.cpp +++ b/Cesium3DTiles/src/BingMapsRasterOverlay.cpp @@ -59,6 +59,7 @@ namespace Cesium3DTiles { Credit bingCredit, const std::vector& perTileCredits, std::shared_ptr pPrepareRendererResources, + std::shared_ptr pLogger, const std::string& baseUrl, const std::string& urlTemplate, const std::vector& subdomains, @@ -73,6 +74,7 @@ namespace Cesium3DTiles { asyncSystem, bingCredit, pPrepareRendererResources, + pLogger, WebMercatorProjection(), QuadtreeTilingScheme( WebMercatorProjection::computeMaximumProjectedRectangle(Ellipsoid::WGS84), @@ -207,6 +209,11 @@ namespace Cesium3DTiles { ](std::unique_ptr pRequest) -> std::unique_ptr { IAssetResponse* pResponse = pRequest->response(); + if (pResponse == nullptr) { + SPDLOG_LOGGER_ERROR(pLogger, "No response received from Bing Maps imagery metadata service."); + return nullptr; + } + rapidjson::Document response; response.Parse(reinterpret_cast(pResponse->data().data()), pResponse->data().size()); @@ -290,6 +297,7 @@ namespace Cesium3DTiles { bingCredit, credits, pPrepareRendererResources, + pLogger, baseUrl, urlTemplate, subdomains, diff --git a/Cesium3DTiles/src/Gltf.cpp b/Cesium3DTiles/src/Gltf.cpp index e50aa3af4..47eb954d6 100644 --- a/Cesium3DTiles/src/Gltf.cpp +++ b/Cesium3DTiles/src/Gltf.cpp @@ -1,35 +1,19 @@ #include "Cesium3DTiles/Gltf.h" namespace Cesium3DTiles { - Gltf::LoadResult Gltf::load(const gsl::span& data) - { - tinygltf::TinyGLTF loader; - tinygltf::Model model; - std::string errors; - std::string warnings; - - loader.LoadBinaryFromMemory(&model, &errors, &warnings, data.data(), static_cast(data.size())); - - return LoadResult{ - model, - warnings, - errors - }; - } - - /*static*/ void Gltf::forEachPrimitiveInScene(tinygltf::Model& gltf, int sceneID, std::function&& callback) { - return Gltf::forEachPrimitiveInScene(const_cast(gltf), sceneID, [&callback]( - const tinygltf::Model& gltf_, - const tinygltf::Node& node, - const tinygltf::Mesh& mesh, - const tinygltf::Primitive& primitive, + /*static*/ void Gltf::forEachPrimitiveInScene(CesiumGltf::Model& gltf, int sceneID, std::function&& callback) { + return Gltf::forEachPrimitiveInScene(const_cast(gltf), sceneID, [&callback]( + const CesiumGltf::Model& gltf_, + const CesiumGltf::Node& node, + const CesiumGltf::Mesh& mesh, + const CesiumGltf::MeshPrimitive& primitive, const glm::dmat4& transform ) { callback( - const_cast(gltf_), - const_cast(node), - const_cast(mesh), - const_cast(primitive), + const_cast(gltf_), + const_cast(node), + const_cast(mesh), + const_cast(primitive), transform ); }); @@ -51,17 +35,17 @@ namespace Cesium3DTiles { static void forEachPrimitiveInMeshObject( const glm::dmat4x4& transform, - const tinygltf::Model& model, - const tinygltf::Node& node, - const tinygltf::Mesh& mesh, + const CesiumGltf::Model& model, + const CesiumGltf::Node& node, + const CesiumGltf::Mesh& mesh, std::function& callback ) { - for (const tinygltf::Primitive& primitive : mesh.primitives) { + for (const CesiumGltf::MeshPrimitive& primitive : mesh.primitives) { callback(model, node, mesh, primitive, transform); } } - static void forEachPrimitiveInNodeObject(const glm::dmat4x4& transform, const tinygltf::Model& model, const tinygltf::Node& node, std::function& callback) { + static void forEachPrimitiveInNodeObject(const glm::dmat4x4& transform, const CesiumGltf::Model& model, const CesiumGltf::Node& node, std::function& callback) { glm::dmat4x4 nodeTransform = transform; if (node.matrix.size() > 0) { @@ -82,7 +66,7 @@ namespace Cesium3DTiles { int meshId = node.mesh; if (meshId >= 0 && meshId < static_cast(model.meshes.size())) { - const tinygltf::Mesh& mesh = model.meshes[static_cast(meshId)]; + const CesiumGltf::Mesh& mesh = model.meshes[static_cast(meshId)]; forEachPrimitiveInMeshObject(nodeTransform, model, node, mesh, callback); } @@ -93,7 +77,7 @@ namespace Cesium3DTiles { } } - static void forEachPrimitiveInSceneObject(const glm::dmat4x4& transform, const tinygltf::Model& model, const tinygltf::Scene& scene, std::function& callback) { + static void forEachPrimitiveInSceneObject(const glm::dmat4x4& transform, const CesiumGltf::Model& model, const CesiumGltf::Scene& scene, std::function& callback) { for (int nodeID : scene.nodes) { if (nodeID >= 0 && nodeID < static_cast(model.nodes.size())) { forEachPrimitiveInNodeObject(transform, model, model.nodes[static_cast(nodeID)], callback); @@ -101,7 +85,7 @@ namespace Cesium3DTiles { } } - /*static*/ void Gltf::forEachPrimitiveInScene(const tinygltf::Model& gltf, int sceneID, std::function&& callback) { + /*static*/ void Gltf::forEachPrimitiveInScene(const CesiumGltf::Model& gltf, int sceneID, std::function&& callback) { glm::dmat4x4 rootTransform = gltfAxesToCesiumAxes; if (sceneID >= 0) { @@ -109,20 +93,20 @@ namespace Cesium3DTiles { if (sceneID < static_cast(gltf.scenes.size())) { forEachPrimitiveInSceneObject(rootTransform, gltf, gltf.scenes[static_cast(sceneID)], callback); } - } else if (gltf.defaultScene >= 0 && gltf.defaultScene < static_cast(gltf.scenes.size())) { + } else if (gltf.scene >= 0 && gltf.scene < static_cast(gltf.scenes.size())) { // Use the default scene - forEachPrimitiveInSceneObject(rootTransform, gltf, gltf.scenes[static_cast(gltf.defaultScene)], callback); + forEachPrimitiveInSceneObject(rootTransform, gltf, gltf.scenes[static_cast(gltf.scene)], callback); } else if (gltf.scenes.size() > 0) { // There's no default, so use the first scene - const tinygltf::Scene& defaultScene = gltf.scenes[0]; + const CesiumGltf::Scene& defaultScene = gltf.scenes[0]; forEachPrimitiveInSceneObject(rootTransform, gltf, defaultScene, callback); } else if (gltf.nodes.size() > 0) { // No scenes at all, use the first node as the root node. forEachPrimitiveInNodeObject(rootTransform, gltf, gltf.nodes[0], callback); } else if (gltf.meshes.size() > 0) { // No nodes either, show all the meshes. - for (const tinygltf::Mesh& mesh : gltf.meshes) { - forEachPrimitiveInMeshObject(rootTransform, gltf, tinygltf::Node(), mesh, callback); + for (const CesiumGltf::Mesh& mesh : gltf.meshes) { + forEachPrimitiveInMeshObject(rootTransform, gltf, CesiumGltf::Node(), mesh, callback); } } } diff --git a/Cesium3DTiles/src/GltfContent.cpp b/Cesium3DTiles/src/GltfContent.cpp index a02c22a21..8eed5c24b 100644 --- a/Cesium3DTiles/src/GltfContent.cpp +++ b/Cesium3DTiles/src/GltfContent.cpp @@ -1,8 +1,10 @@ -#include "Cesium3DTiles/GltfAccessor.h" #include "Cesium3DTiles/GltfContent.h" -#include "Cesium3DTiles/GltfWriter.h" #include "Cesium3DTiles/spdlog-cesium.h" +#include "CesiumGltf/AccessorView.h" +#include "CesiumGltf/AccessorWriter.h" +#include "CesiumGltf/Reader.h" #include "CesiumUtility/Math.h" +#include "CesiumUtility/joinToString.h" #include namespace Cesium3DTiles { @@ -16,29 +18,26 @@ namespace Cesium3DTiles { const glm::dmat4& /*tileTransform*/, const std::optional& /*tileContentBoundingVolume*/, TileRefine /*tileRefine*/, - const std::string& /*url*/, + const std::string& url, const gsl::span& data ) { std::unique_ptr pResult = std::make_unique(); - std::string errors; - std::string warnings; - - tinygltf::TinyGLTF loader; - pResult->model.emplace(); - bool success = loader.LoadBinaryFromMemory(&pResult->model.value(), &errors, &warnings, data.data(), static_cast(data.size())); - if (!success) { - SPDLOG_LOGGER_ERROR(pLogger, "Failed to load binary glTF from memory: {}", errors); + CesiumGltf::ModelReaderResult loadedModel = CesiumGltf::readModel(data); + if (!loadedModel.errors.empty()) { + SPDLOG_LOGGER_ERROR(pLogger, "Failed to load binary glTF from {}:\n- {}", url, CesiumUtility::joinToString(loadedModel.errors, "\n- ")); } - if (!warnings.empty()) { - SPDLOG_LOGGER_WARN(pLogger, "Warning when loading binary glTF from memory: {}", warnings); + if (!loadedModel.warnings.empty()) { + SPDLOG_LOGGER_WARN(pLogger, "Warning when loading binary glTF from {}:\n- {}", url, CesiumUtility::joinToString(loadedModel.warnings, "\n- ")); } + pResult->model = std::move(loadedModel.model); + return pResult; } static int generateOverlayTextureCoordinates( - tinygltf::Model& gltf, + CesiumGltf::Model& gltf, int positionAccessorIndex, const glm::dmat4x4& transform, const CesiumGeospatial::Projection& projection, @@ -50,42 +49,49 @@ namespace Cesium3DTiles { double& minimumHeight, double& maximumHeight ) { - int uvBufferId = static_cast(gltf.buffers.size()); - gltf.buffers.emplace_back(); + std::vector& buffers = gltf.buffers; + std::vector& bufferViews = gltf.bufferViews; + std::vector& accessors = gltf.accessors; - int uvBufferViewId = static_cast(gltf.bufferViews.size()); - gltf.bufferViews.emplace_back(); + int uvBufferId = static_cast(buffers.size()); + CesiumGltf::Buffer& uvBuffer = buffers.emplace_back(); - int uvAccessorId = static_cast(gltf.accessors.size()); - gltf.accessors.emplace_back(); + int uvBufferViewId = static_cast(bufferViews.size()); + bufferViews.emplace_back(); - GltfAccessor positionAccessor(gltf, static_cast(positionAccessorIndex)); + int uvAccessorId = static_cast(accessors.size()); + accessors.emplace_back(); + + CesiumGltf::AccessorView positionView(gltf, positionAccessorIndex); + if (positionView.status() != CesiumGltf::AccessorViewStatus::Valid) { + return -1; + } - tinygltf::Buffer& uvBuffer = gltf.buffers[static_cast(uvBufferId)]; - uvBuffer.data.resize(positionAccessor.size() * 2 * sizeof(float)); + uvBuffer.cesium.data.resize(size_t(positionView.size()) * 2 * sizeof(float)); - tinygltf::BufferView& uvBufferView = gltf.bufferViews[static_cast(uvBufferViewId)]; + CesiumGltf::BufferView& uvBufferView = gltf.bufferViews[static_cast(uvBufferViewId)]; uvBufferView.buffer = uvBufferId; uvBufferView.byteOffset = 0; uvBufferView.byteStride = 2 * sizeof(float); - uvBufferView.byteLength = uvBuffer.data.size(); - uvBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER; + uvBufferView.byteLength = int64_t(uvBuffer.cesium.data.size()); + uvBufferView.target = CesiumGltf::BufferView::Target::ARRAY_BUFFER; - tinygltf::Accessor& uvAccessor = gltf.accessors[static_cast(uvAccessorId)]; + CesiumGltf::Accessor& uvAccessor = gltf.accessors[static_cast(uvAccessorId)]; uvAccessor.bufferView = uvBufferViewId; uvAccessor.byteOffset = 0; - uvAccessor.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT; - uvAccessor.count = positionAccessor.size(); - uvAccessor.type = TINYGLTF_TYPE_VEC2; + uvAccessor.componentType = CesiumGltf::Accessor::ComponentType::FLOAT; + uvAccessor.count = int64_t(positionView.size()); + uvAccessor.type = CesiumGltf::Accessor::Type::VEC2; - GltfWriter uvWriter(gltf, static_cast(uvAccessorId)); + CesiumGltf::AccessorWriter uvWriter(gltf, uvAccessorId); + assert(uvWriter.status() == CesiumGltf::AccessorViewStatus::Valid); double width = rectangle.computeWidth(); double height = rectangle.computeHeight(); - for (size_t i = 0; i < positionAccessor.size(); ++i) { + for (int64_t i = 0; i < positionView.size(); ++i) { // Get the ECEF position - glm::vec3 position = positionAccessor[i]; + glm::vec3 position = positionView[i]; glm::dvec3 positionEcef = transform * glm::dvec4(position, 1.0); // Convert it to cartographic @@ -151,7 +157,7 @@ namespace Cesium3DTiles { } /*static*/ CesiumGeospatial::BoundingRegion GltfContent::createRasterOverlayTextureCoordinates( - tinygltf::Model& gltf, + CesiumGltf::Model& gltf, uint32_t textureCoordinateID, const CesiumGeospatial::Projection& projection, const CesiumGeometry::Rectangle& rectangle @@ -169,10 +175,10 @@ namespace Cesium3DTiles { double maximumHeight = std::numeric_limits::lowest(); Gltf::forEachPrimitiveInScene(gltf, -1, [&positionAccessorsToTextureCoordinateAccessor, &attributeName, &projection, &rectangle, &west, &south, &east, &north, &minimumHeight, &maximumHeight]( - tinygltf::Model& gltf_, - tinygltf::Node& /*node*/, - tinygltf::Mesh& /*mesh*/, - tinygltf::Primitive& primitive, + CesiumGltf::Model& gltf_, + CesiumGltf::Node& /*node*/, + CesiumGltf::Mesh& /*mesh*/, + CesiumGltf::MeshPrimitive& primitive, const glm::dmat4& transform ) { auto positionIt = primitive.attributes.find("POSITION"); @@ -198,6 +204,10 @@ namespace Cesium3DTiles { // Generate new texture coordinates int nextTextureCoordinateAccessorIndex = generateOverlayTextureCoordinates(gltf_, positionAccessorIndex, transform, projection, rectangle, west, south, east, north, minimumHeight, maximumHeight); + if (nextTextureCoordinateAccessorIndex < 0) { + return; + } + primitive.attributes[attributeName] = nextTextureCoordinateAccessorIndex; positionAccessorsToTextureCoordinateAccessor[static_cast(positionAccessorIndex)] = nextTextureCoordinateAccessorIndex; }); diff --git a/Cesium3DTiles/src/QuantizedMeshContent.cpp b/Cesium3DTiles/src/QuantizedMeshContent.cpp index b54553b87..6280492c6 100644 --- a/Cesium3DTiles/src/QuantizedMeshContent.cpp +++ b/Cesium3DTiles/src/QuantizedMeshContent.cpp @@ -7,7 +7,6 @@ #include "QuantizedMeshContent.h" #include "calcQuadtreeMaxGeometricError.h" #include "SkirtMeshMetadata.h" -#include "tiny_gltf.h" #include "Uri.h" #include "JsonHelpers.h" #include @@ -831,69 +830,69 @@ namespace Cesium3DTiles { // create gltf pResult->model.emplace(); - tinygltf::Model& model = pResult->model.value(); + CesiumGltf::Model& model = pResult->model.value(); size_t meshId = model.meshes.size(); model.meshes.emplace_back(); - tinygltf::Mesh& mesh = model.meshes[meshId]; + CesiumGltf::Mesh& mesh = model.meshes[meshId]; mesh.primitives.emplace_back(); - tinygltf::Primitive& primitive = mesh.primitives[0]; - primitive.mode = TINYGLTF_MODE_TRIANGLES; + CesiumGltf::MeshPrimitive& primitive = mesh.primitives[0]; + primitive.mode = CesiumGltf::MeshPrimitive::Mode::TRIANGLES; primitive.material = 0; // add position buffer to gltf size_t positionBufferId = model.buffers.size(); model.buffers.emplace_back(); - tinygltf::Buffer& positionBuffer = model.buffers[positionBufferId]; - positionBuffer.data = std::move(outputPositionsBuffer); + CesiumGltf::Buffer& positionBuffer = model.buffers[positionBufferId]; + positionBuffer.cesium.data = std::move(outputPositionsBuffer); size_t positionBufferViewId = model.bufferViews.size(); model.bufferViews.emplace_back(); - tinygltf::BufferView& positionBufferView = model.bufferViews[positionBufferViewId]; - positionBufferView.buffer = static_cast(positionBufferId); + CesiumGltf::BufferView& positionBufferView = model.bufferViews[positionBufferViewId]; + positionBufferView.buffer = int32_t(positionBufferId); positionBufferView.byteOffset = 0; positionBufferView.byteStride = 3 * sizeof(float); - positionBufferView.byteLength = positionBuffer.data.size(); - positionBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER; + positionBufferView.byteLength = int64_t(positionBuffer.cesium.data.size()); + positionBufferView.target = CesiumGltf::BufferView::Target::ARRAY_BUFFER; size_t positionAccessorId = model.accessors.size(); model.accessors.emplace_back(); - tinygltf::Accessor& positionAccessor = model.accessors[positionAccessorId]; + CesiumGltf::Accessor& positionAccessor = model.accessors[positionAccessorId]; positionAccessor.bufferView = static_cast(positionBufferViewId); positionAccessor.byteOffset = 0; - positionAccessor.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT; + positionAccessor.componentType = CesiumGltf::Accessor::ComponentType::FLOAT; positionAccessor.count = vertexCount + skirtVertexCount; - positionAccessor.type = TINYGLTF_TYPE_VEC3; - positionAccessor.minValues = { minX, minY, minZ }; - positionAccessor.maxValues = { maxX, maxY, maxZ }; + positionAccessor.type = CesiumGltf::Accessor::Type::VEC3; + positionAccessor.min = { minX, minY, minZ }; + positionAccessor.max = { maxX, maxY, maxZ }; - primitive.attributes.emplace("POSITION", static_cast(positionAccessorId)); + primitive.attributes.emplace("POSITION", int32_t(positionAccessorId)); // add normal buffer to gltf if there are any if (!outputNormalsBuffer.empty()) { size_t normalBufferId = model.buffers.size(); model.buffers.emplace_back(); - tinygltf::Buffer& normalBuffer = model.buffers[normalBufferId]; - normalBuffer.data = std::move(outputNormalsBuffer); + CesiumGltf::Buffer& normalBuffer = model.buffers[normalBufferId]; + normalBuffer.cesium.data = std::move(outputNormalsBuffer); size_t normalBufferViewId = model.bufferViews.size(); model.bufferViews.emplace_back(); - tinygltf::BufferView& normalBufferView = model.bufferViews[normalBufferViewId]; - normalBufferView.buffer = static_cast(normalBufferId); + CesiumGltf::BufferView& normalBufferView = model.bufferViews[normalBufferViewId]; + normalBufferView.buffer = int32_t(normalBufferId); normalBufferView.byteOffset = 0; normalBufferView.byteStride = 3 * sizeof(float); - normalBufferView.byteLength = normalBuffer.data.size(); - normalBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER; + normalBufferView.byteLength = int64_t(normalBuffer.cesium.data.size()); + normalBufferView.target = CesiumGltf::BufferView::Target::ARRAY_BUFFER; size_t normalAccessorId = model.accessors.size(); model.accessors.emplace_back(); - tinygltf::Accessor& normalAccessor = model.accessors[normalAccessorId]; - normalAccessor.bufferView = static_cast(normalBufferViewId); + CesiumGltf::Accessor& normalAccessor = model.accessors[normalAccessorId]; + normalAccessor.bufferView = int32_t(normalBufferViewId); normalAccessor.byteOffset = 0; - normalAccessor.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT; + normalAccessor.componentType = CesiumGltf::Accessor::ComponentType::FLOAT; normalAccessor.count = vertexCount + skirtVertexCount; - normalAccessor.type = TINYGLTF_TYPE_VEC3; + normalAccessor.type = CesiumGltf::Accessor::Type::VEC3; primitive.attributes.emplace("NORMAL", static_cast(normalAccessorId)); } @@ -901,28 +900,28 @@ namespace Cesium3DTiles { // add indices buffer to gltf size_t indicesBufferId = model.buffers.size(); model.buffers.emplace_back(); - tinygltf::Buffer& indicesBuffer = model.buffers[indicesBufferId]; - indicesBuffer.data = std::move(outputIndicesBuffer); + CesiumGltf::Buffer& indicesBuffer = model.buffers[indicesBufferId]; + indicesBuffer.cesium.data = std::move(outputIndicesBuffer); size_t indicesBufferViewId = model.bufferViews.size(); model.bufferViews.emplace_back(); - tinygltf::BufferView& indicesBufferView = model.bufferViews[indicesBufferViewId]; - indicesBufferView.buffer = static_cast(indicesBufferId); + CesiumGltf::BufferView& indicesBufferView = model.bufferViews[indicesBufferViewId]; + indicesBufferView.buffer = int32_t(indicesBufferId); indicesBufferView.byteOffset = 0; - indicesBufferView.byteLength = indicesBuffer.data.size(); + indicesBufferView.byteLength = int64_t(indicesBuffer.cesium.data.size()); indicesBufferView.byteStride = indexSizeBytes; - indicesBufferView.target = TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER; + indicesBufferView.target = CesiumGltf::BufferView::Target::ARRAY_BUFFER; size_t indicesAccessorId = model.accessors.size(); model.accessors.emplace_back(); - tinygltf::Accessor& indicesAccessor = model.accessors[indicesAccessorId]; - indicesAccessor.bufferView = static_cast(indicesBufferViewId); + CesiumGltf::Accessor& indicesAccessor = model.accessors[indicesAccessorId]; + indicesAccessor.bufferView = int32_t(indicesBufferViewId); indicesAccessor.byteOffset = 0; - indicesAccessor.type = TINYGLTF_TYPE_SCALAR; + indicesAccessor.type = CesiumGltf::Accessor::Type::SCALAR; indicesAccessor.count = indicesCount + skirtIndicesCount; - indicesAccessor.componentType = indexSizeBytes == sizeof(uint32_t) ? TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT : TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT; + indicesAccessor.componentType = indexSizeBytes == sizeof(uint32_t) ? CesiumGltf::Accessor::ComponentType::UNSIGNED_INT : CesiumGltf::Accessor::ComponentType::UNSIGNED_SHORT; - primitive.indices = static_cast(indicesBufferId); + primitive.indices = int32_t(indicesBufferId); // add skirts info to primitive extra in case we need to upsample from it SkirtMeshMetadata skirtMeshMetadata; @@ -938,7 +937,7 @@ namespace Cesium3DTiles { // create node and update bounding volume model.nodes.emplace_back(); - tinygltf::Node& node = model.nodes[0]; + CesiumGltf::Node& node = model.nodes[0]; node.mesh = 0; node.matrix = { 1.0, 0.0, 0.0, 0.0, diff --git a/Cesium3DTiles/src/RasterOverlayTile.cpp b/Cesium3DTiles/src/RasterOverlayTile.cpp index 5fb5fcf50..91705ad74 100644 --- a/Cesium3DTiles/src/RasterOverlayTile.cpp +++ b/Cesium3DTiles/src/RasterOverlayTile.cpp @@ -5,6 +5,8 @@ #include "Cesium3DTiles/TilesetExternals.h" #include "CesiumAsync/IAssetResponse.h" #include "CesiumAsync/ITaskProcessor.h" +#include "CesiumGltf/Reader.h" +#include "CesiumUtility/joinToString.h" using namespace CesiumAsync; @@ -48,9 +50,9 @@ namespace Cesium3DTiles { {} LoadState state; - tinygltf::Image image; - std::string warnings; - std::string errors; + CesiumGltf::ImageCesium image; + std::vector warnings; + std::vector errors; void* pRendererResources; }; @@ -63,7 +65,8 @@ namespace Cesium3DTiles { tileRectangle = pTileProvider->getTilingScheme().tileToRectangle(this->getID()), projection = pTileProvider->getProjection(), cutoutsCollection = overlay.getCutouts(), - pPrepareRendererResources = pTileProvider->getPrepareRendererResources() + pPrepareRendererResources = pTileProvider->getPrepareRendererResources(), + pLogger = pTileProvider->getLogger() ]( std::unique_ptr pRequest ) { @@ -76,21 +79,35 @@ namespace Cesium3DTiles { return LoadResult(LoadState::Failed); } - LoadResult result; - gsl::span data = pResponse->data(); - bool success = tinygltf::LoadImageData(&result.image, 0, &result.errors, &result.warnings, 0, 0, data.data(), static_cast(data.size()), nullptr); + CesiumGltf::ImageReaderResult loadedImage = CesiumGltf::readImage(data); + + if (!loadedImage.image.has_value()) { + SPDLOG_LOGGER_ERROR(pLogger, "Failed to load image:\n- {}", CesiumUtility::joinToString(loadedImage.errors, "\n- ")); + + LoadResult result; + result.pRendererResources = nullptr; + result.state = LoadState::Failed; + return result; + } - const int bytesPerPixel = 4; - if (success && result.image.image.size() >= static_cast(result.image.width * result.image.height * bytesPerPixel)) { + if (!loadedImage.warnings.empty()) { + SPDLOG_LOGGER_WARN(pLogger, "Warnings while loading image:\n- {}", CesiumUtility::joinToString(loadedImage.warnings, "\n- ")); + } + + CesiumGltf::ImageCesium& image = loadedImage.image.value(); + + int32_t bytesPerPixel = image.channels * image.bytesPerChannel; + + if (image.pixelData.size() >= static_cast(image.width * image.height * bytesPerPixel)) { double tileWidth = tileRectangle.computeWidth(); double tileHeight = tileRectangle.computeHeight(); gsl::span cutouts = cutoutsCollection.getCutouts(); - std::vector& imageData = result.image.image; - int width = result.image.width; - int height = result.image.width; + std::vector& imageData = image.pixelData; + int width = image.width; + int height = image.height; for (const CesiumGeospatial::GlobeRectangle& rectangle : cutouts) { CesiumGeometry::Rectangle cutoutRectangle = projectRectangleSimple(projection, rectangle); @@ -111,31 +128,37 @@ namespace Cesium3DTiles { std::swap(startV, endV); - uint32_t startPixelX = static_cast(std::floor(startU * width)); - uint32_t endPixelX = static_cast(std::ceil(endU * width)); - uint32_t startPixelY = static_cast(std::floor(startV * height)); - uint32_t endPixelY = static_cast(std::ceil(endV * height)); + int32_t startPixelX = static_cast(std::floor(startU * width)); + int32_t endPixelX = static_cast(std::ceil(endU * width)); + int32_t startPixelY = static_cast(std::floor(startV * height)); + int32_t endPixelY = static_cast(std::ceil(endV * height)); - for (uint32_t j = startPixelY; j < endPixelY; ++j) { - uint32_t rowStart = j * static_cast(width) * static_cast(bytesPerPixel); - for (uint32_t i = startPixelX; i < endPixelX; ++i) { - uint32_t pixelStart = rowStart + i * bytesPerPixel; + for (int32_t j = startPixelY; j < endPixelY; ++j) { + int32_t rowStart = j * width * bytesPerPixel; + for (int32_t i = startPixelX; i < endPixelX; ++i) { + int32_t pixelStart = rowStart + i * bytesPerPixel; // Set alpha to 0 - imageData[pixelStart + 3] = 0; + imageData[size_t(pixelStart + 3)] = 0; } } } - result.pRendererResources = pPrepareRendererResources->prepareRasterInLoadThread(result.image); + void* pRendererResources = pPrepareRendererResources->prepareRasterInLoadThread(image); + LoadResult result; result.state = LoadState::Loaded; + result.image = std::move(image); + result.pRendererResources = pRendererResources; + result.errors = std::move(loadedImage.errors); + result.warnings = std::move(loadedImage.warnings); + return result; } else { + LoadResult result; result.pRendererResources = nullptr; result.state = LoadState::Failed; + return result; } - - return result; }).thenInMainThread([this](LoadResult&& result) { result.pRendererResources = result.pRendererResources; this->_image = std::move(result.image); diff --git a/Cesium3DTiles/src/RasterOverlayTileProvider.cpp b/Cesium3DTiles/src/RasterOverlayTileProvider.cpp index b8b1b2ba5..35fce1d2b 100644 --- a/Cesium3DTiles/src/RasterOverlayTileProvider.cpp +++ b/Cesium3DTiles/src/RasterOverlayTileProvider.cpp @@ -17,6 +17,7 @@ namespace Cesium3DTiles { _asyncSystem(asyncSystem), _credit(std::nullopt), _pPrepareRendererResources(nullptr), + _pLogger(nullptr), _projection(CesiumGeospatial::GeographicProjection()), _tilingScheme(CesiumGeometry::QuadtreeTilingScheme(CesiumGeometry::Rectangle(0.0, 0.0, 0.0, 0.0), 1, 1)), _coverageRectangle(0.0, 0.0, 0.0, 0.0), @@ -37,6 +38,7 @@ namespace Cesium3DTiles { const AsyncSystem& asyncSystem, std::optional credit, std::shared_ptr pPrepareRendererResources, + std::shared_ptr pLogger, const CesiumGeospatial::Projection& projection, const CesiumGeometry::QuadtreeTilingScheme& tilingScheme, const CesiumGeometry::Rectangle& coverageRectangle, @@ -49,6 +51,7 @@ namespace Cesium3DTiles { _asyncSystem(asyncSystem), _credit(credit), _pPrepareRendererResources(pPrepareRendererResources), + _pLogger(pLogger), _projection(projection), _tilingScheme(tilingScheme), _coverageRectangle(coverageRectangle), @@ -353,7 +356,7 @@ namespace Cesium3DTiles { } void RasterOverlayTileProvider::notifyTileLoaded(RasterOverlayTile* pTile) noexcept { - this->_tileDataBytes += pTile->getImage().image.size(); + this->_tileDataBytes += int64_t(pTile->getImage().pixelData.size()); --this->_tilesCurrentlyLoading; } @@ -364,7 +367,7 @@ namespace Cesium3DTiles { assert(it != this->_tiles.end()); assert(it->second.get() == pTile); - this->_tileDataBytes -= pTile->getImage().image.size(); + this->_tileDataBytes -= int64_t(pTile->getImage().pixelData.size()); RasterOverlay& overlay = pTile->getOverlay(); diff --git a/Cesium3DTiles/src/SkirtMeshMetadata.cpp b/Cesium3DTiles/src/SkirtMeshMetadata.cpp index 0782d6f29..f489466b5 100644 --- a/Cesium3DTiles/src/SkirtMeshMetadata.cpp +++ b/Cesium3DTiles/src/SkirtMeshMetadata.cpp @@ -1,131 +1,78 @@ #include "SkirtMeshMetadata.h" +using namespace CesiumGltf; + namespace Cesium3DTiles { - std::optional SkirtMeshMetadata::parseFromGltfExtras(const tinygltf::Value& extras) { - if (extras.IsObject() && extras.Has("skirtMeshMetadata")) { - SkirtMeshMetadata skirtMeshMetadata; - - tinygltf::Value gltfSkirtMeshMetadata = extras.Get("skirtMeshMetadata"); - if (!gltfSkirtMeshMetadata.IsObject()) { - return std::nullopt; - } - - if (gltfSkirtMeshMetadata.Has("noSkirtRange")) { - tinygltf::Value noSkirtRange = gltfSkirtMeshMetadata.Get("noSkirtRange"); - if (noSkirtRange.IsArray() && noSkirtRange.ArrayLen() == 2) { - tinygltf::Value noSkirtIndicesBegin = noSkirtRange.Get(0); - tinygltf::Value noSkirtIndicesCount = noSkirtRange.Get(1); - if (noSkirtIndicesBegin.IsInt() && noSkirtIndicesCount.IsInt()) { - skirtMeshMetadata.noSkirtIndicesBegin = static_cast(noSkirtRange.Get(0).GetNumberAsInt()); - skirtMeshMetadata.noSkirtIndicesCount = static_cast(noSkirtRange.Get(1).GetNumberAsInt()); - } - else { - return std::nullopt; - } - } - else { - return std::nullopt; - } - } - else { - return std::nullopt; - } - - if (gltfSkirtMeshMetadata.Has("meshCenter")) { - tinygltf::Value meshCenter = gltfSkirtMeshMetadata.Get("meshCenter"); - if (meshCenter.IsArray() && meshCenter.ArrayLen() == 3) { - tinygltf::Value x = meshCenter.Get(0); - tinygltf::Value y = meshCenter.Get(1); - tinygltf::Value z = meshCenter.Get(2); - if (x.IsNumber() && y.IsNumber() && z.IsNumber()) { - skirtMeshMetadata.meshCenter.x = x.GetNumberAsDouble(); - skirtMeshMetadata.meshCenter.y = y.GetNumberAsDouble(); - skirtMeshMetadata.meshCenter.z = z.GetNumberAsDouble(); - } - else { - return std::nullopt; - } - } - else { - return std::nullopt; - } - } - else { - return std::nullopt; - } - - if (gltfSkirtMeshMetadata.Has("skirtWestHeight")) { - tinygltf::Value skirtWestHeight = gltfSkirtMeshMetadata.Get("skirtWestHeight"); - if (skirtWestHeight.IsNumber()) { - skirtMeshMetadata.skirtWestHeight = skirtWestHeight.GetNumberAsDouble(); - } - else { - return std::nullopt; - } - } - else { - return std::nullopt; - } - - if (gltfSkirtMeshMetadata.Has("skirtSouthHeight")) { - tinygltf::Value skirtSouthHeight = gltfSkirtMeshMetadata.Get("skirtSouthHeight"); - if (skirtSouthHeight.IsNumber()) { - skirtMeshMetadata.skirtSouthHeight = skirtSouthHeight.GetNumberAsDouble(); - } - else { - return std::nullopt; - } - } - else { - return std::nullopt; - } - - if (gltfSkirtMeshMetadata.Has("skirtEastHeight")) { - tinygltf::Value skirtEastHeight = gltfSkirtMeshMetadata.Get("skirtEastHeight"); - if (skirtEastHeight.IsNumber()) { - skirtMeshMetadata.skirtEastHeight = skirtEastHeight.GetNumberAsDouble(); - } - else { - return std::nullopt; - } - } - else { - return std::nullopt; - } - - if (gltfSkirtMeshMetadata.Has("skirtNorthHeight")) { - tinygltf::Value skirtNorthHeight = gltfSkirtMeshMetadata.Get("skirtNorthHeight"); - if (skirtNorthHeight.IsNumber()) { - skirtMeshMetadata.skirtNorthHeight = skirtNorthHeight.GetNumberAsDouble(); - } - else { - return std::nullopt; - } - } - else { - return std::nullopt; - } - - return skirtMeshMetadata; + std::optional SkirtMeshMetadata::parseFromGltfExtras(const JsonValue::Object& extras) { + auto skirtIt = extras.find("skirtMeshMetadata"); + if (skirtIt == extras.end()) { + return std::nullopt; } - return std::nullopt; - } + SkirtMeshMetadata skirtMeshMetadata; + + const JsonValue& gltfSkirtMeshMetadata = skirtIt->second; + const JsonValue::Array* pNoSkirtRange = gltfSkirtMeshMetadata.getValueForKey("noSkirtRange"); + if (!pNoSkirtRange || pNoSkirtRange->size() != 2) { + return std::nullopt; + } + + if (!(*pNoSkirtRange)[0].isNumber() || !(*pNoSkirtRange)[1].isNumber()) { + return std::nullopt; + } + + double noSkirtIndicesBegin = (*pNoSkirtRange)[0].getNumber(-1.0); + double noSkirtIndicesCount = (*pNoSkirtRange)[1].getNumber(-1.0); + + if (noSkirtIndicesBegin < 0.0 || noSkirtIndicesCount < 0.0) { + return std::nullopt; + } - tinygltf::Value SkirtMeshMetadata::createGltfExtras(const SkirtMeshMetadata& skirtMeshMetadata) { - tinygltf::Value::Object gltfSkirtMeshMetadata; - gltfSkirtMeshMetadata.insert({ "noSkirtRange", tinygltf::Value(tinygltf::Value::Array({ - tinygltf::Value(static_cast(skirtMeshMetadata.noSkirtIndicesBegin)), tinygltf::Value(static_cast(skirtMeshMetadata.noSkirtIndicesCount))})) }); + skirtMeshMetadata.noSkirtIndicesBegin = static_cast(noSkirtIndicesBegin); + skirtMeshMetadata.noSkirtIndicesCount = static_cast(noSkirtIndicesCount); - gltfSkirtMeshMetadata.insert({ "meshCenter", tinygltf::Value(tinygltf::Value::Array({ - tinygltf::Value(skirtMeshMetadata.meshCenter.x), tinygltf::Value(skirtMeshMetadata.meshCenter.y), tinygltf::Value(skirtMeshMetadata.meshCenter.z)})) }); + const JsonValue::Array* pMeshCenter = gltfSkirtMeshMetadata.getValueForKey("meshCenter"); + if (!pMeshCenter || pMeshCenter->size() != 3) { + return std::nullopt; + } + + if (!(*pMeshCenter)[0].isNumber() || !(*pMeshCenter)[1].isNumber() || !(*pMeshCenter)[2].isNumber()) { + return std::nullopt; + } + + skirtMeshMetadata.meshCenter = glm::dvec3( + (*pMeshCenter)[0].getNumber(0.0), + (*pMeshCenter)[1].getNumber(0.0), + (*pMeshCenter)[2].getNumber(0.0) + ); + + const double* pWestHeight = gltfSkirtMeshMetadata.getValueForKey("skirtWestHeight"); + const double* pSouthHeight = gltfSkirtMeshMetadata.getValueForKey("skirtSouthHeight"); + const double* pEastHeight = gltfSkirtMeshMetadata.getValueForKey("skirtEastHeight"); + const double* pNorthHeight = gltfSkirtMeshMetadata.getValueForKey("skirtNorthHeight"); - gltfSkirtMeshMetadata.insert({ "skirtWestHeight", tinygltf::Value(skirtMeshMetadata.skirtWestHeight) }); - gltfSkirtMeshMetadata.insert({ "skirtSouthHeight", tinygltf::Value(skirtMeshMetadata.skirtSouthHeight) }); - gltfSkirtMeshMetadata.insert({ "skirtEastHeight", tinygltf::Value(skirtMeshMetadata.skirtEastHeight) }); - gltfSkirtMeshMetadata.insert({ "skirtNorthHeight", tinygltf::Value(skirtMeshMetadata.skirtNorthHeight) }); + if (!pWestHeight || !pSouthHeight || !pEastHeight || !pNorthHeight) { + return std::nullopt; + } + + skirtMeshMetadata.skirtWestHeight = *pWestHeight; + skirtMeshMetadata.skirtSouthHeight = *pSouthHeight; + skirtMeshMetadata.skirtEastHeight = *pEastHeight; + skirtMeshMetadata.skirtNorthHeight = *pNorthHeight; + + return skirtMeshMetadata; + } - return tinygltf::Value( - tinygltf::Value::Object{ {"skirtMeshMetadata", tinygltf::Value(gltfSkirtMeshMetadata)} }); + CesiumGltf::JsonValue::Object SkirtMeshMetadata::createGltfExtras(const SkirtMeshMetadata& skirtMeshMetadata) { + return { + { "skirtMeshMetadata", JsonValue::Object { + { "noSkirtRange", JsonValue::Array { skirtMeshMetadata.noSkirtIndicesBegin, skirtMeshMetadata.noSkirtIndicesCount } }, + { "meshCenter", JsonValue::Array { skirtMeshMetadata.meshCenter.x, skirtMeshMetadata.meshCenter.y, skirtMeshMetadata.meshCenter.z } }, + { "skirtWestHeight", skirtMeshMetadata.skirtWestHeight }, + { "skirtSouthHeight", skirtMeshMetadata.skirtSouthHeight }, + { "skirtEastHeight", skirtMeshMetadata.skirtEastHeight }, + { "skirtNorthHeight", skirtMeshMetadata.skirtNorthHeight } + } } + }; } } \ No newline at end of file diff --git a/Cesium3DTiles/src/SkirtMeshMetadata.h b/Cesium3DTiles/src/SkirtMeshMetadata.h index 327a68f68..b4f22ced3 100644 --- a/Cesium3DTiles/src/SkirtMeshMetadata.h +++ b/Cesium3DTiles/src/SkirtMeshMetadata.h @@ -1,4 +1,5 @@ -#include "Cesium3DTiles/Gltf.h" +#include "CesiumGltf/JsonValue.h" +#include #include namespace Cesium3DTiles { @@ -13,9 +14,9 @@ namespace Cesium3DTiles { skirtNorthHeight{0.0} {} - static std::optional parseFromGltfExtras(const tinygltf::Value &extras); + static std::optional parseFromGltfExtras(const CesiumGltf::JsonValue::Object& extras); - static tinygltf::Value createGltfExtras(const SkirtMeshMetadata &skirt); + static CesiumGltf::JsonValue::Object createGltfExtras(const SkirtMeshMetadata &skirt); uint32_t noSkirtIndicesBegin; uint32_t noSkirtIndicesCount; diff --git a/Cesium3DTiles/src/Tile.cpp b/Cesium3DTiles/src/Tile.cpp index d07b04212..b17346fc7 100644 --- a/Cesium3DTiles/src/Tile.cpp +++ b/Cesium3DTiles/src/Tile.cpp @@ -290,11 +290,13 @@ namespace Cesium3DTiles { this->_pRendererResources = loadResult.pRendererResources; this->getTileset()->notifyTileDoneLoading(this); this->setState(loadResult.state); - }).catchInMainThread([this](const std::exception& /*e*/) { + }).catchInMainThread([this](const std::exception& e) { this->_pContent.reset(); this->_pRendererResources = nullptr; this->getTileset()->notifyTileDoneLoading(this); this->setState(LoadState::Failed); + + SPDLOG_LOGGER_ERROR(this->getTileset()->getExternals().pLogger, "An exception occurred while loading tile: {}", e.what()); }); } @@ -577,29 +579,29 @@ namespace Cesium3DTiles { } } - size_t Tile::computeByteSize() const noexcept { - size_t bytes = 0; + int64_t Tile::computeByteSize() const noexcept { + int64_t bytes = 0; const TileContentLoadResult* pContent = this->getContent(); if (pContent && pContent->model) { - const tinygltf::Model& model = pContent->model.value(); + const CesiumGltf::Model& model = pContent->model.value(); // Add up the glTF buffers - for (const tinygltf::Buffer& buffer : model.buffers) { - bytes += buffer.data.size(); + for (const CesiumGltf::Buffer& buffer : model.buffers) { + bytes += int64_t(buffer.cesium.data.size()); } // For images loaded from buffers, subtract the buffer size and add // the decoded image size instead. - const std::vector& bufferViews = model.bufferViews; - for (const tinygltf::Image& image : model.images) { - int bufferView = image.bufferView; - if (bufferView < 0 || bufferView >= static_cast(bufferViews.size())) { + const std::vector& bufferViews = model.bufferViews; + for (const CesiumGltf::Image& image : model.images) { + int32_t bufferView = image.bufferView; + if (bufferView < 0 || bufferView >= static_cast(bufferViews.size())) { continue; } - bytes -= bufferViews[static_cast(bufferView)].byteLength; - bytes += image.image.size(); + bytes -= bufferViews[size_t(bufferView)].byteLength; + bytes += int64_t(image.cesium.pixelData.size()); } } @@ -611,7 +613,7 @@ namespace Cesium3DTiles { } /*static*/ std::optional Tile::generateTextureCoordinates( - tinygltf::Model& model, + CesiumGltf::Model& model, const BoundingVolume& boundingVolume, const std::vector& projections ) { @@ -655,7 +657,7 @@ namespace Cesium3DTiles { return; } - tinygltf::Model& parentModel = pParentContent->model.value(); + CesiumGltf::Model& parentModel = pParentContent->model.value(); Tileset* pTileset = this->getTileset(); pTileset->notifyTileStartLoading(this); diff --git a/Cesium3DTiles/src/TileMapServiceRasterOverlay.cpp b/Cesium3DTiles/src/TileMapServiceRasterOverlay.cpp index b08fd683e..679083ac3 100644 --- a/Cesium3DTiles/src/TileMapServiceRasterOverlay.cpp +++ b/Cesium3DTiles/src/TileMapServiceRasterOverlay.cpp @@ -22,6 +22,7 @@ namespace Cesium3DTiles { const AsyncSystem& asyncSystem, std::optional credit, std::shared_ptr pPrepareRendererResources, + std::shared_ptr pLogger, const CesiumGeospatial::Projection& projection, const CesiumGeometry::QuadtreeTilingScheme& tilingScheme, const CesiumGeometry::Rectangle& coverageRectangle, @@ -38,6 +39,7 @@ namespace Cesium3DTiles { asyncSystem, credit, pPrepareRendererResources, + pLogger, projection, tilingScheme, coverageRectangle, @@ -247,6 +249,7 @@ namespace Cesium3DTiles { asyncSystem, credit, pPrepareRendererResources, + pLogger, projection, tilingScheme, coverageRectangle, diff --git a/Cesium3DTiles/src/Tileset.cpp b/Cesium3DTiles/src/Tileset.cpp index 6441765c0..fe35d5021 100644 --- a/Cesium3DTiles/src/Tileset.cpp +++ b/Cesium3DTiles/src/Tileset.cpp @@ -347,8 +347,8 @@ namespace Cesium3DTiles { } } - size_t Tileset::getTotalDataBytes() const noexcept { - size_t bytes = this->_tileDataBytes; + int64_t Tileset::getTotalDataBytes() const noexcept { + int64_t bytes = this->_tileDataBytes; for (auto& pOverlay : this->_overlays) { const RasterOverlayTileProvider* pProvider = pOverlay->getTileProvider(); @@ -1200,7 +1200,7 @@ namespace Cesium3DTiles { } void Tileset::_unloadCachedTiles() { - const size_t maxBytes = this->getOptions().maximumCachedBytes; + const int64_t maxBytes = this->getOptions().maximumCachedBytes; Tile* pTile = this->_loadedTiles.head(); diff --git a/Cesium3DTiles/src/tinygltf.cpp b/Cesium3DTiles/src/tinygltf.cpp deleted file mode 100644 index 476f9b64c..000000000 --- a/Cesium3DTiles/src/tinygltf.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#define TINYGLTF_USE_CPP14 -#define TINYGLTF_IMPLEMENTATION -#define STB_IMAGE_IMPLEMENTATION -#define STB_IMAGE_WRITE_IMPLEMENTATION -//#define TINYGLTF_NOEXCEPTION // optional. disable exception handling. -#define TINYGLTF_ENABLE_DRACO -#define TINYGLTF_USE_RAPIDJSON -#define TINYGLTF_NO_INCLUDE_JSON -#define TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR - -#ifdef _MSC_VER -#pragma warning(disable:4996 4946 4100 4189 4127) -#endif - -#include -#include -#include -#include -#include -#include diff --git a/Cesium3DTiles/src/upsampleGltfForRasterOverlays.cpp b/Cesium3DTiles/src/upsampleGltfForRasterOverlays.cpp index 5a650d0e9..b3b584a68 100644 --- a/Cesium3DTiles/src/upsampleGltfForRasterOverlays.cpp +++ b/Cesium3DTiles/src/upsampleGltfForRasterOverlays.cpp @@ -1,12 +1,14 @@ -#include "Cesium3DTiles/GltfAccessor.h" #include "CesiumGeometry/clipTriangleAtAxisAlignedThreshold.h" -#include "CesiumUtility/Math.h" -#include "CesiumGeospatial/Ellipsoid.h" #include "CesiumGeospatial/Cartographic.h" -#include "upsampleGltfForRasterOverlays.h" +#include "CesiumGeospatial/Ellipsoid.h" +#include "CesiumGltf/AccessorView.h" +#include "CesiumUtility/Math.h" #include "SkirtMeshMetadata.h" +#include "upsampleGltfForRasterOverlays.h" #include +using namespace CesiumGltf; + namespace Cesium3DTiles { struct EdgeVertex { uint32_t index; @@ -21,18 +23,18 @@ namespace Cesium3DTiles { }; static void upsamplePrimitiveForRasterOverlays( - const tinygltf::Model& parentModel, - tinygltf::Model& model, - tinygltf::Mesh& mesh, - tinygltf::Primitive& primitive, + const Model& parentModel, + Model& model, + Mesh& mesh, + MeshPrimitive& primitive, CesiumGeometry::QuadtreeChild childID ); struct FloatVertexAttribute { const std::vector& buffer; - size_t offset; - int32_t stride; - int32_t numberOfFloatsPerVertex; + int64_t offset; + int64_t stride; + int64_t numberOfFloatsPerVertex; int32_t accessorIndex; std::vector minimums; std::vector maximums; @@ -53,7 +55,7 @@ namespace Cesium3DTiles { double thresholdV, bool keepAboveU, bool keepAboveV, - const GltfAccessor& uvs, + const AccessorView& uvs, const std::vector& clipVertexToIndices, const std::vector& complements, const std::vector& clipResult); @@ -64,8 +66,8 @@ namespace Cesium3DTiles { const std::vector& edgeIndices, const glm::dvec3& center, double skirtHeight, - size_t vertexSizeFloats, - uint32_t positionAttributeIndex); + int64_t vertexSizeFloats, + int32_t positionAttributeIndex); static void addSkirts(std::vector& output, std::vector& indices, @@ -74,11 +76,11 @@ namespace Cesium3DTiles { SkirtMeshMetadata ¤tSkirt, const SkirtMeshMetadata &parentSkirt, EdgeIndices &edgeIndices, - size_t vertexSizeFloats, - uint32_t positionAttributeIndex); + int64_t vertexSizeFloats, + int32_t positionAttributeIndex); - tinygltf::Model upsampleGltfForRasterOverlays(const tinygltf::Model& parentModel, CesiumGeometry::QuadtreeChild childID) { - tinygltf::Model result; + Model upsampleGltfForRasterOverlays(const Model& parentModel, CesiumGeometry::QuadtreeChild childID) { + Model result; // Copy the entire parent model except for the buffers, bufferViews, and accessors, which we'll be rewriting. result.animations = parentModel.animations; @@ -91,18 +93,17 @@ namespace Cesium3DTiles { result.samplers = parentModel.samplers; result.cameras = parentModel.cameras; result.scenes = parentModel.scenes; - result.lights = parentModel.lights; - result.defaultScene = parentModel.defaultScene; + result.scene = parentModel.scene; result.extensionsUsed = parentModel.extensionsUsed; result.extensionsRequired = parentModel.extensionsRequired; result.asset = parentModel.asset; - result.extras = parentModel.extras; - result.extensions = parentModel.extensions; - result.extras_json_string = parentModel.extras_json_string; - result.extensions_json_string = parentModel.extensions_json_string; + // result.extras = parentModel.extras; + // result.extensions = parentModel.extensions; + // result.extras_json_string = parentModel.extras_json_string; + // result.extensions_json_string = parentModel.extensions_json_string; - for (tinygltf::Mesh& mesh : result.meshes) { - for (tinygltf::Primitive& primitive : mesh.primitives) { + for (Mesh& mesh : result.meshes) { + for (MeshPrimitive& primitive : mesh.primitives) { upsamplePrimitiveForRasterOverlays(parentModel, result, mesh, primitive, childID); } } @@ -217,17 +218,17 @@ namespace Cesium3DTiles { } template - static T getVertexValue(const GltfAccessor& accessor, const CesiumGeometry::TriangleClipVertex& vertex) { + static T getVertexValue(const AccessorView& accessor, const CesiumGeometry::TriangleClipVertex& vertex) { struct Operation { - const Cesium3DTiles::GltfAccessor& accessor; + const AccessorView& accessor; T operator()(int vertexIndex) { - return accessor[static_cast(vertexIndex)]; + return accessor[vertexIndex]; } T operator()(const CesiumGeometry::InterpolatedVertex& vertex) { - const T& v0 = accessor[static_cast(vertex.first)]; - const T& v1 = accessor[static_cast(vertex.second)]; + const T& v0 = accessor[vertex.first]; + const T& v1 = accessor[vertex.second]; return glm::mix(v0, v1, vertex.t); } }; @@ -236,12 +237,12 @@ namespace Cesium3DTiles { } template - static T getVertexValue(const GltfAccessor& accessor, + static T getVertexValue(const AccessorView& accessor, const std::vector &complements, const CesiumGeometry::TriangleClipVertex& vertex) { struct Operation { - const Cesium3DTiles::GltfAccessor& accessor; + const AccessorView& accessor; const std::vector& complements; T operator()(int vertexIndex) { @@ -249,7 +250,7 @@ namespace Cesium3DTiles { return getVertexValue(accessor, complements, complements[static_cast(~vertexIndex)]); } - return accessor[static_cast(vertexIndex)]; + return accessor[vertexIndex]; } T operator()(const CesiumGeometry::InterpolatedVertex& vertex) { @@ -258,7 +259,7 @@ namespace Cesium3DTiles { v0 = getVertexValue(accessor, complements, complements[static_cast(~vertex.first)]); } else { - v0 = accessor[static_cast(vertex.first)]; + v0 = accessor[vertex.first]; } T v1{}; @@ -266,7 +267,7 @@ namespace Cesium3DTiles { v1 = getVertexValue(accessor, complements, complements[static_cast(~vertex.second)]); } else { - v1 = accessor[static_cast(vertex.second)]; + v1 = accessor[vertex.second]; } return glm::mix(v0, v1, vertex.t); @@ -278,10 +279,10 @@ namespace Cesium3DTiles { template static void upsamplePrimitiveForRasterOverlays( - const tinygltf::Model& parentModel, - tinygltf::Model& model, - tinygltf::Mesh& /*mesh*/, - tinygltf::Primitive& primitive, + const Model& parentModel, + Model& model, + Mesh& /*mesh*/, + MeshPrimitive& primitive, CesiumGeometry::QuadtreeChild childID ) { // Add up the per-vertex size of all attributes and create buffers, bufferViews, and accessors @@ -300,17 +301,17 @@ namespace Cesium3DTiles { size_t indexBufferViewIndex = model.bufferViews.size(); model.bufferViews.emplace_back(); - tinygltf::BufferView& vertexBufferView = model.bufferViews[vertexBufferViewIndex]; + BufferView& vertexBufferView = model.bufferViews[vertexBufferViewIndex]; vertexBufferView.buffer = static_cast(vertexBufferIndex); - vertexBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER; + vertexBufferView.target = BufferView::Target::ARRAY_BUFFER; - tinygltf::BufferView& indexBufferView = model.bufferViews[indexBufferViewIndex]; + BufferView& indexBufferView = model.bufferViews[indexBufferViewIndex]; indexBufferView.buffer = static_cast(indexBufferIndex); - indexBufferView.target = TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER; + indexBufferView.target = BufferView::Target::ARRAY_BUFFER; - uint32_t vertexSizeFloats = 0; - int uvAccessorIndex = -1; - int positionAttributeIndex = -1; + int64_t vertexSizeFloats = 0; + int32_t uvAccessorIndex = -1; + int32_t positionAttributeIndex = -1; std::vector toRemove; @@ -334,39 +335,39 @@ namespace Cesium3DTiles { continue; } - const tinygltf::Accessor& accessor = parentModel.accessors[static_cast(attribute.second)]; + const Accessor& accessor = parentModel.accessors[static_cast(attribute.second)]; if (accessor.bufferView < 0 || accessor.bufferView >= static_cast(parentModel.bufferViews.size())) { toRemove.push_back(attribute.first); continue; } - const tinygltf::BufferView& bufferView = parentModel.bufferViews[static_cast(accessor.bufferView)]; + const BufferView& bufferView = parentModel.bufferViews[static_cast(accessor.bufferView)]; if (bufferView.buffer < 0 || bufferView.buffer >= static_cast(parentModel.buffers.size())) { toRemove.push_back(attribute.first); continue; } - const tinygltf::Buffer& buffer = parentModel.buffers[static_cast(bufferView.buffer)]; + const Buffer& buffer = parentModel.buffers[static_cast(bufferView.buffer)]; - int32_t accessorByteStride = accessor.ByteStride(bufferView); - int32_t accessorComponentElements = tinygltf::GetNumComponentsInType(static_cast(accessor.type)); - if (accessor.componentType != TINYGLTF_COMPONENT_TYPE_FLOAT) { + int64_t accessorByteStride = accessor.computeByteStride(parentModel); + int64_t accessorComponentElements = accessor.computeNumberOfComponents(); + if (accessor.componentType != Accessor::ComponentType::FLOAT) { // Can only interpolate floating point vertex attributes return; } attribute.second = static_cast(model.accessors.size()); model.accessors.emplace_back(); - tinygltf::Accessor& newAccessor = model.accessors.back(); + Accessor& newAccessor = model.accessors.back(); newAccessor.bufferView = static_cast(vertexBufferIndex); - newAccessor.byteOffset = vertexSizeFloats * sizeof(float); - newAccessor.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT; + newAccessor.byteOffset = vertexSizeFloats * int64_t(sizeof(float)); + newAccessor.componentType = Accessor::ComponentType::FLOAT; newAccessor.type = accessor.type; - vertexSizeFloats += static_cast(accessorComponentElements); + vertexSizeFloats += accessorComponentElements; attributes.push_back(FloatVertexAttribute { - buffer.data, + buffer.cesium.data, accessor.byteOffset, accessorByteStride, accessorComponentElements, @@ -377,7 +378,7 @@ namespace Cesium3DTiles { // get position to be used to create for skirts later if (attribute.first == "POSITION") { - positionAttributeIndex = static_cast(attributes.size() - 1); + positionAttributeIndex = int32_t(attributes.size() - 1); } } @@ -394,12 +395,16 @@ namespace Cesium3DTiles { bool keepAboveU = childID == CesiumGeometry::QuadtreeChild::LowerRight || childID == CesiumGeometry::QuadtreeChild::UpperRight; bool keepAboveV = childID == CesiumGeometry::QuadtreeChild::UpperLeft || childID == CesiumGeometry::QuadtreeChild::UpperRight; - GltfAccessor uvAccessor(parentModel, static_cast(uvAccessorIndex)); - GltfAccessor indicesAccessor(parentModel, static_cast(primitive.indices)); + AccessorView uvView(parentModel, uvAccessorIndex); + AccessorView indicesView(parentModel, primitive.indices); + + if (uvView.status() != AccessorViewStatus::Valid || indicesView.status() != AccessorViewStatus::Valid) { + return; + } // check if the primitive has skirts - size_t indicesBegin = 0; - size_t indicesCount = indicesAccessor.size(); + int64_t indicesBegin = 0; + int64_t indicesCount = indicesView.size(); std::optional parentSkirtMeshMetadata = SkirtMeshMetadata::parseFromGltfExtras(primitive.extras); bool hasSkirt = (parentSkirtMeshMetadata != std::nullopt) && (positionAttributeIndex != -1); if (hasSkirt) { @@ -412,7 +417,7 @@ namespace Cesium3DTiles { std::vector clippedB; // Maps old (parentModel) vertex indices to new (model) vertex indices. - std::vector vertexMap(uvAccessor.size(), std::numeric_limits::max()); + std::vector vertexMap(size_t(uvView.size()), std::numeric_limits::max()); // std::vector newVertexBuffer(vertexSizeFloats * sizeof(float)); // gsl::span newVertexFloats(reinterpret_cast(newVertexBuffer.data()), newVertexBuffer.size() / sizeof(float)); @@ -420,14 +425,14 @@ namespace Cesium3DTiles { std::vector indices; EdgeIndices edgeIndices; - for (size_t i = indicesBegin; i < indicesBegin + indicesCount; i += 3) { - TIndex i0 = indicesAccessor[i]; - TIndex i1 = indicesAccessor[i + 1]; - TIndex i2 = indicesAccessor[i + 2]; + for (int64_t i = indicesBegin; i < indicesBegin + indicesCount; i += 3) { + TIndex i0 = indicesView[i]; + TIndex i1 = indicesView[i + 1]; + TIndex i2 = indicesView[i + 2]; - glm::vec2 uv0 = uvAccessor[i0]; - glm::vec2 uv1 = uvAccessor[i1]; - glm::vec2 uv2 = uvAccessor[i2]; + glm::vec2 uv0 = uvView[i0]; + glm::vec2 uv1 = uvView[i1]; + glm::vec2 uv2 = uvView[i2]; // Clip this triangle against the East-West boundary clippedA.clear(); @@ -447,16 +452,16 @@ namespace Cesium3DTiles { ~0, ~1, ~2, - getVertexValue(uvAccessor, clippedA[0]).y, - getVertexValue(uvAccessor, clippedA[1]).y, - getVertexValue(uvAccessor, clippedA[2]).y, + getVertexValue(uvView, clippedA[0]).y, + getVertexValue(uvView, clippedA[1]).y, + getVertexValue(uvView, clippedA[2]).y, clippedB ); // Add the clipped triangle or quad, if any addClippedPolygon(newVertexFloats, indices, attributes, vertexMap, clipVertexToIndices, clippedA, clippedB); if (hasSkirt) { - addEdge(edgeIndices, 0.5, 0.5, keepAboveU, keepAboveV, uvAccessor, clipVertexToIndices, clippedA, clippedB); + addEdge(edgeIndices, 0.5, 0.5, keepAboveU, keepAboveV, uvView, clipVertexToIndices, clippedA, clippedB); } // If the East-West clip yielded a quad (rather than a triangle), clip the second triangle of the quad, too. @@ -469,16 +474,16 @@ namespace Cesium3DTiles { ~0, ~2, ~3, - getVertexValue(uvAccessor, clippedA[0]).y, - getVertexValue(uvAccessor, clippedA[2]).y, - getVertexValue(uvAccessor, clippedA[3]).y, + getVertexValue(uvView, clippedA[0]).y, + getVertexValue(uvView, clippedA[2]).y, + getVertexValue(uvView, clippedA[3]).y, clippedB ); // Add the clipped triangle or quad, if any addClippedPolygon(newVertexFloats, indices, attributes, vertexMap, clipVertexToIndices, clippedA, clippedB); if (hasSkirt) { - addEdge(edgeIndices, 0.5, 0.5, keepAboveU, keepAboveV, uvAccessor, clipVertexToIndices, clippedA, clippedB); + addEdge(edgeIndices, 0.5, 0.5, keepAboveU, keepAboveV, uvView, clipVertexToIndices, clippedA, clippedB); } } } @@ -498,41 +503,41 @@ namespace Cesium3DTiles { *parentSkirtMeshMetadata, edgeIndices, vertexSizeFloats, - static_cast(positionAttributeIndex)); + positionAttributeIndex); } // Update the accessor vertex counts and min/max values - size_t numberOfVertices = newVertexFloats.size() / vertexSizeFloats; + int64_t numberOfVertices = int64_t(newVertexFloats.size()) / vertexSizeFloats; for (const FloatVertexAttribute& attribute : attributes) { - tinygltf::Accessor& accessor = model.accessors[static_cast(attribute.accessorIndex)]; + Accessor& accessor = model.accessors[static_cast(attribute.accessorIndex)]; accessor.count = numberOfVertices; - accessor.minValues = std::move(attribute.minimums); - accessor.maxValues = std::move(attribute.maximums); + accessor.min = std::move(attribute.minimums); + accessor.max = std::move(attribute.maximums); } // Add an accessor for the indices size_t indexAccessorIndex = model.accessors.size(); model.accessors.emplace_back(); - tinygltf::Accessor& newIndicesAccessor = model.accessors.back(); + Accessor& newIndicesAccessor = model.accessors.back(); newIndicesAccessor.bufferView = static_cast(indexBufferViewIndex); newIndicesAccessor.byteOffset = 0; - newIndicesAccessor.count = indices.size(); - newIndicesAccessor.componentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT; - newIndicesAccessor.type = TINYGLTF_TYPE_SCALAR; + newIndicesAccessor.count = int64_t(indices.size()); + newIndicesAccessor.componentType = Accessor::ComponentType::UNSIGNED_INT; + newIndicesAccessor.type = Accessor::Type::SCALAR; // Populate the buffers - tinygltf::Buffer& vertexBuffer = model.buffers[vertexBufferIndex]; - vertexBuffer.data.resize(newVertexFloats.size() * sizeof(float)); - float* pAsFloats = reinterpret_cast(vertexBuffer.data.data()); + Buffer& vertexBuffer = model.buffers[vertexBufferIndex]; + vertexBuffer.cesium.data.resize(newVertexFloats.size() * sizeof(float)); + float* pAsFloats = reinterpret_cast(vertexBuffer.cesium.data.data()); std::copy(newVertexFloats.begin(), newVertexFloats.end(), pAsFloats); - vertexBufferView.byteLength = vertexBuffer.data.size(); - vertexBufferView.byteStride = vertexSizeFloats * sizeof(float); + vertexBufferView.byteLength = int64_t(vertexBuffer.cesium.data.size()); + vertexBufferView.byteStride = vertexSizeFloats * int64_t(sizeof(float)); - tinygltf::Buffer& indexBuffer = model.buffers[indexBufferIndex]; - indexBuffer.data.resize(indices.size() * sizeof(uint32_t)); - uint32_t* pAsUint32s = reinterpret_cast(indexBuffer.data.data()); + Buffer& indexBuffer = model.buffers[indexBufferIndex]; + indexBuffer.cesium.data.resize(indices.size() * sizeof(uint32_t)); + uint32_t* pAsUint32s = reinterpret_cast(indexBuffer.cesium.data.data()); std::copy(indices.begin(), indices.end(), pAsUint32s); - indexBufferView.byteLength = indexBuffer.data.size(); + indexBufferView.byteLength = int64_t(indexBuffer.cesium.data.size()); // add skirts to extras to be upsampled later if needed if (hasSkirt) { @@ -613,7 +618,7 @@ namespace Cesium3DTiles { double thresholdV, bool keepAboveU, bool keepAboveV, - const GltfAccessor& uvs, + const AccessorView& uvs, const std::vector& clipVertexToIndices, const std::vector& complements, const std::vector& clipResult) @@ -663,20 +668,20 @@ namespace Cesium3DTiles { const std::vector& edgeIndices, const glm::dvec3& center, double skirtHeight, - size_t vertexSizeFloats, - uint32_t positionAttributeIndex) + int64_t vertexSizeFloats, + int32_t positionAttributeIndex) { const CesiumGeospatial::Ellipsoid& ellipsoid = CesiumGeospatial::Ellipsoid::WGS84; - uint32_t newEdgeIndex = static_cast(output.size() / vertexSizeFloats); - for (uint32_t i = 0; i < edgeIndices.size(); ++i) { + uint32_t newEdgeIndex = uint32_t(output.size() / size_t(vertexSizeFloats)); + for (size_t i = 0; i < edgeIndices.size(); ++i) { uint32_t edgeIdx = edgeIndices[i]; uint32_t offset = 0; - for (uint32_t j = 0; j < attributes.size(); ++j) { + for (size_t j = 0; j < attributes.size(); ++j) { FloatVertexAttribute& attribute = attributes[j]; - uint32_t valueIndex = offset + static_cast(vertexSizeFloats) * edgeIdx; + uint32_t valueIndex = offset + uint32_t(vertexSizeFloats) * edgeIdx; - if (j == positionAttributeIndex) { + if (int32_t(j) == positionAttributeIndex) { glm::dvec3 position{ output[valueIndex], output[valueIndex + 1], output[valueIndex + 2] }; position += center; @@ -722,8 +727,8 @@ namespace Cesium3DTiles { SkirtMeshMetadata ¤tSkirt, const SkirtMeshMetadata &parentSkirt, EdgeIndices &edgeIndices, - size_t vertexSizeFloats, - uint32_t positionAttributeIndex) + int64_t vertexSizeFloats, + int32_t positionAttributeIndex) { glm::dvec3 center = currentSkirt.meshCenter; double shortestSkirtHeight = glm::min(parentSkirt.skirtWestHeight, parentSkirt.skirtEastHeight); @@ -840,14 +845,14 @@ namespace Cesium3DTiles { } static void upsamplePrimitiveForRasterOverlays( - const tinygltf::Model& parentModel, - tinygltf::Model& model, - tinygltf::Mesh& mesh, - tinygltf::Primitive& primitive, + const Model& parentModel, + Model& model, + Mesh& mesh, + MeshPrimitive& primitive, CesiumGeometry::QuadtreeChild childID ) { if ( - primitive.mode != TINYGLTF_MODE_TRIANGLES || + primitive.mode != MeshPrimitive::Mode::TRIANGLES || primitive.indices < 0 || primitive.indices >= static_cast(parentModel.accessors.size()) ) { @@ -857,10 +862,10 @@ namespace Cesium3DTiles { return; } - const tinygltf::Accessor& indicesAccessorGltf = parentModel.accessors[static_cast(primitive.indices)]; - if (indicesAccessorGltf.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) { + const Accessor& indicesAccessorGltf = parentModel.accessors[static_cast(primitive.indices)]; + if (indicesAccessorGltf.componentType == Accessor::ComponentType::UNSIGNED_SHORT) { upsamplePrimitiveForRasterOverlays(parentModel, model, mesh, primitive, childID); - } else if (indicesAccessorGltf.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) { + } else if (indicesAccessorGltf.componentType == Accessor::ComponentType::UNSIGNED_INT) { upsamplePrimitiveForRasterOverlays(parentModel, model, mesh, primitive, childID); } } diff --git a/Cesium3DTiles/src/upsampleGltfForRasterOverlays.h b/Cesium3DTiles/src/upsampleGltfForRasterOverlays.h index 657afe1b8..2ab75fbd8 100644 --- a/Cesium3DTiles/src/upsampleGltfForRasterOverlays.h +++ b/Cesium3DTiles/src/upsampleGltfForRasterOverlays.h @@ -5,6 +5,6 @@ namespace Cesium3DTiles { - tinygltf::Model upsampleGltfForRasterOverlays(const tinygltf::Model& parentModel, CesiumGeometry::QuadtreeChild childID); + CesiumGltf::Model upsampleGltfForRasterOverlays(const CesiumGltf::Model& parentModel, CesiumGeometry::QuadtreeChild childID); } diff --git a/Cesium3DTiles/test/TestQuantizedMeshContent.cpp b/Cesium3DTiles/test/TestQuantizedMeshContent.cpp index 7ac0ae6bf..c624c81eb 100644 --- a/Cesium3DTiles/test/TestQuantizedMeshContent.cpp +++ b/Cesium3DTiles/test/TestQuantizedMeshContent.cpp @@ -1,16 +1,17 @@ #include "catch2/catch.hpp" -#include "Cesium3DTiles/GltfAccessor.h" #include "Cesium3DTiles/registerAllTileContentTypes.h" #include "Cesium3DTiles/spdlog-cesium.h" #include "Cesium3DTiles/TileContentFactory.h" #include "CesiumGeometry/QuadtreeTilingScheme.h" #include "CesiumGeometry/Rectangle.h" +#include "CesiumGltf/AccessorView.h" #include "CesiumUtility/Math.h" -#include "glm/glm.hpp" +#include using namespace Cesium3DTiles; using namespace CesiumGeometry; using namespace CesiumGeospatial; +using namespace CesiumGltf; using namespace CesiumUtility; struct QuantizedMeshHeader { @@ -312,8 +313,8 @@ static QuantizedMesh createGridQuantizedMesh(const BoundingRegion ®ion, ui template void checkGridMesh(const QuantizedMesh& quantizedMesh, - const GltfAccessor& indices, - const GltfAccessor& positions, + const AccessorView& indices, + const AccessorView& positions, const QuadtreeTilingScheme &tilingScheme, const Ellipsoid &ellipsoid, const Rectangle &tileRectangle, @@ -389,8 +390,8 @@ void checkGridMesh(const QuantizedMesh& quantizedMesh, double longitudeOffset = (west - east) * 0.0001; double latitudeOffset = (north - south) * 0.0001; - REQUIRE(totalSkirtIndices == indices.size() - gridIndicesCount); - REQUIRE(totalSkirtVertices == positions.size() - gridVerticesCount); + REQUIRE(totalSkirtIndices == size_t(indices.size()) - gridIndicesCount); + REQUIRE(totalSkirtVertices == size_t(positions.size()) - gridVerticesCount); size_t currentVertexCount = gridVerticesCount; for (size_t i = 0; i < westIndicesCount; ++i) { @@ -399,7 +400,7 @@ void checkGridMesh(const QuantizedMesh& quantizedMesh, double latitude = Math::lerp(south, north, uvs[westIndex].y); glm::dvec3 expectPosition = ellipsoid.cartographicToCartesian(Cartographic(longitude, latitude, -skirtHeight)); - glm::dvec3 position = static_cast(positions[currentVertexCount + i]); + glm::dvec3 position = static_cast(positions[int64_t(currentVertexCount + i)]); position += glm::dvec3(quantizedMesh.header.boundingSphereCenterX, quantizedMesh.header.boundingSphereCenterY, quantizedMesh.header.boundingSphereCenterZ); REQUIRE(Math::equalsEpsilon(position.x, expectPosition.x, Math::EPSILON3)); REQUIRE(Math::equalsEpsilon(position.y, expectPosition.y, Math::EPSILON3)); @@ -413,7 +414,7 @@ void checkGridMesh(const QuantizedMesh& quantizedMesh, double latitude = south - latitudeOffset; glm::dvec3 expectPosition = ellipsoid.cartographicToCartesian(Cartographic(longitude, latitude, -skirtHeight)); - glm::dvec3 position = static_cast(positions[currentVertexCount + i]); + glm::dvec3 position = static_cast(positions[int64_t(currentVertexCount + i)]); position += glm::dvec3(quantizedMesh.header.boundingSphereCenterX, quantizedMesh.header.boundingSphereCenterY, quantizedMesh.header.boundingSphereCenterZ); REQUIRE(Math::equalsEpsilon(position.x, expectPosition.x, Math::EPSILON3)); REQUIRE(Math::equalsEpsilon(position.y, expectPosition.y, Math::EPSILON3)); @@ -427,7 +428,7 @@ void checkGridMesh(const QuantizedMesh& quantizedMesh, double latitude = Math::lerp(south, north, uvs[eastIndex].y); glm::dvec3 expectPosition = ellipsoid.cartographicToCartesian(Cartographic(longitude, latitude, -skirtHeight)); - glm::dvec3 position = static_cast(positions[currentVertexCount + i]); + glm::dvec3 position = static_cast(positions[int64_t(currentVertexCount + i)]); position += glm::dvec3(quantizedMesh.header.boundingSphereCenterX, quantizedMesh.header.boundingSphereCenterY, quantizedMesh.header.boundingSphereCenterZ); REQUIRE(Math::equalsEpsilon(position.x, expectPosition.x, Math::EPSILON3)); REQUIRE(Math::equalsEpsilon(position.y, expectPosition.y, Math::EPSILON2)); @@ -441,7 +442,7 @@ void checkGridMesh(const QuantizedMesh& quantizedMesh, double latitude = north + latitudeOffset; glm::dvec3 expectPosition = ellipsoid.cartographicToCartesian(Cartographic(longitude, latitude, -skirtHeight)); - glm::dvec3 position = static_cast(positions[currentVertexCount + i]); + glm::dvec3 position = static_cast(positions[int64_t(currentVertexCount + i)]); position += glm::dvec3(quantizedMesh.header.boundingSphereCenterX, quantizedMesh.header.boundingSphereCenterY, quantizedMesh.header.boundingSphereCenterZ); REQUIRE(Math::equalsEpsilon(position.x, expectPosition.x, Math::EPSILON3)); REQUIRE(Math::equalsEpsilon(position.y, expectPosition.y, Math::EPSILON3)); @@ -451,9 +452,9 @@ void checkGridMesh(const QuantizedMesh& quantizedMesh, template static void checkGeneratedGridNormal(const QuantizedMesh& quantizedMesh, - const GltfAccessor &normals, - const GltfAccessor &positions, - const GltfAccessor &indices, + const AccessorView &normals, + const AccessorView &positions, + const AccessorView &indices, const glm::vec3 &geodeticNormal, uint32_t verticesWidth, uint32_t verticesHeight) @@ -477,7 +478,7 @@ static void checkGeneratedGridNormal(const QuantizedMesh& quantizedMesh, for (size_t i = 0; i < expectedNormals.size(); ++i) { glm::vec3 &expectedNormal = expectedNormals[i]; - glm::vec3 normal = normals[i]; + glm::vec3 normal = normals[int64_t(i)]; if (!Math::equalsEpsilon(glm::dot(expectedNormals[i], expectedNormals[i]), 0.0, Math::EPSILON7)) { expectedNormal = glm::normalize(expectedNormals[i]); @@ -505,14 +506,14 @@ static void checkGeneratedGridNormal(const QuantizedMesh& quantizedMesh, size_t gridVerticesCount = verticesWidth * verticesHeight; size_t totalSkirtVertices = westIndicesCount + southIndicesCount + eastIndicesCount + northIndicesCount; - REQUIRE(totalSkirtVertices == normals.size() - gridVerticesCount); + REQUIRE(totalSkirtVertices == size_t(normals.size()) - gridVerticesCount); size_t currentVertexCount = gridVerticesCount; uint32_t x = 0; uint32_t y = 0; for (size_t i = 0; i < westIndicesCount; ++i) { - glm::vec3 normal = normals[currentVertexCount + i]; - glm::vec3 expectedNormal = expectedNormals[static_cast(index2DTo1D(x, y, verticesWidth))]; + glm::vec3 normal = normals[int64_t(currentVertexCount + i)]; + glm::vec3 expectedNormal = expectedNormals[index2DTo1D(x, y, verticesWidth)]; REQUIRE(Math::equalsEpsilon(normal.x, expectedNormal.x, Math::EPSILON7)); REQUIRE(Math::equalsEpsilon(normal.y, expectedNormal.y, Math::EPSILON7)); REQUIRE(Math::equalsEpsilon(normal.z, expectedNormal.z, Math::EPSILON7)); @@ -524,8 +525,8 @@ static void checkGeneratedGridNormal(const QuantizedMesh& quantizedMesh, x = verticesWidth - 1; y = 0; for (size_t i = 0; i < southIndicesCount; ++i) { - glm::vec3 normal = normals[currentVertexCount + i]; - glm::vec3 expectedNormal = expectedNormals[static_cast(index2DTo1D(x, y, verticesWidth))]; + glm::vec3 normal = normals[int64_t(currentVertexCount + i)]; + glm::vec3 expectedNormal = expectedNormals[index2DTo1D(x, y, verticesWidth)]; REQUIRE(Math::equalsEpsilon(normal.x, expectedNormal.x, Math::EPSILON7)); REQUIRE(Math::equalsEpsilon(normal.y, expectedNormal.y, Math::EPSILON7)); REQUIRE(Math::equalsEpsilon(normal.z, expectedNormal.z, Math::EPSILON7)); @@ -537,8 +538,8 @@ static void checkGeneratedGridNormal(const QuantizedMesh& quantizedMesh, x = verticesWidth - 1; y = verticesHeight - 1; for (size_t i = 0; i < eastIndicesCount; ++i) { - glm::vec3 normal = normals[currentVertexCount + i]; - glm::vec3 expectedNormal = expectedNormals[static_cast(index2DTo1D(x, y, verticesWidth))]; + glm::vec3 normal = normals[int64_t(currentVertexCount + i)]; + glm::vec3 expectedNormal = expectedNormals[index2DTo1D(x, y, verticesWidth)]; REQUIRE(Math::equalsEpsilon(normal.x, expectedNormal.x, Math::EPSILON7)); REQUIRE(Math::equalsEpsilon(normal.y, expectedNormal.y, Math::EPSILON7)); REQUIRE(Math::equalsEpsilon(normal.z, expectedNormal.z, Math::EPSILON7)); @@ -550,8 +551,8 @@ static void checkGeneratedGridNormal(const QuantizedMesh& quantizedMesh, x = 0; y = verticesHeight - 1; for (size_t i = 0; i < northIndicesCount; ++i) { - glm::vec3 normal = normals[currentVertexCount + i]; - glm::vec3 expectedNormal = expectedNormals[static_cast(index2DTo1D(x, y, verticesWidth))]; + glm::vec3 normal = normals[int64_t(currentVertexCount + i)]; + glm::vec3 expectedNormal = expectedNormals[index2DTo1D(x, y, verticesWidth)]; REQUIRE(Math::equalsEpsilon(normal.x, expectedNormal.x, Math::EPSILON7)); REQUIRE(Math::equalsEpsilon(normal.y, expectedNormal.y, Math::EPSILON7)); REQUIRE(Math::equalsEpsilon(normal.z, expectedNormal.z, Math::EPSILON7)); @@ -606,17 +607,22 @@ TEST_CASE("Test converting quantized mesh to gltf with skirt") { REQUIRE(loadResult->model != std::nullopt); // make sure the gltf is the grid - const tinygltf::Model& model = *loadResult->model; - const tinygltf::Mesh& mesh = model.meshes.front(); - const tinygltf::Primitive& primitive = mesh.primitives.front(); + const CesiumGltf::Model& model = *loadResult->model; + const CesiumGltf::Mesh& mesh = model.meshes.front(); + const CesiumGltf::MeshPrimitive& primitive = mesh.primitives.front(); // make sure mesh contains grid mesh and skirts at the end - GltfAccessor indices(model, static_cast(primitive.indices)); - GltfAccessor positions(model, static_cast(primitive.attributes.at("POSITION"))); + AccessorView indices(model, primitive.indices); + CHECK(indices.status() == AccessorViewStatus::Valid); + AccessorView positions(model, primitive.attributes.at("POSITION")); + CHECK(positions.status() == AccessorViewStatus::Valid); + checkGridMesh(quantizedMesh, indices, positions, tilingScheme, ellipsoid, tileRectangle, verticesWidth, verticesHeight); // check normal - GltfAccessor normals(model, static_cast(primitive.attributes.at("NORMAL"))); + AccessorView normals(model, primitive.attributes.at("NORMAL")); + CHECK(normals.status() == AccessorViewStatus::Valid); + Cartographic center = boundingVolume.getRectangle().computeCenter(); glm::vec3 geodeticNormal = static_cast(ellipsoid.geodeticSurfaceNormal(center)); checkGeneratedGridNormal(quantizedMesh, normals, positions, indices, geodeticNormal, verticesWidth, verticesHeight); @@ -653,17 +659,22 @@ TEST_CASE("Test converting quantized mesh to gltf with skirt") { REQUIRE(loadResult->model != std::nullopt); // make sure the gltf is the grid - const tinygltf::Model& model = *loadResult->model; - const tinygltf::Mesh& mesh = model.meshes.front(); - const tinygltf::Primitive& primitive = mesh.primitives.front(); + const CesiumGltf::Model& model = *loadResult->model; + const CesiumGltf::Mesh& mesh = model.meshes.front(); + const CesiumGltf::MeshPrimitive& primitive = mesh.primitives.front(); // make sure mesh contains grid mesh and skirts at the end - GltfAccessor indices(model, static_cast(primitive.indices)); - GltfAccessor positions(model, static_cast(primitive.attributes.at("POSITION"))); + AccessorView indices(model, primitive.indices); + CHECK(indices.status() == AccessorViewStatus::Valid); + AccessorView positions(model, primitive.attributes.at("POSITION")); + CHECK(positions.status() == AccessorViewStatus::Valid); + checkGridMesh(quantizedMesh, indices, positions, tilingScheme, ellipsoid, tileRectangle, verticesWidth, verticesHeight); // check normal - GltfAccessor normals(model, static_cast(primitive.attributes.at("NORMAL"))); + AccessorView normals(model, primitive.attributes.at("NORMAL")); + CHECK(normals.status() == AccessorViewStatus::Valid); + Cartographic center = boundingVolume.getRectangle().computeCenter(); glm::vec3 geodeticNormal = static_cast(ellipsoid.geodeticSurfaceNormal(center)); checkGeneratedGridNormal(quantizedMesh, normals, positions, indices, geodeticNormal, verticesWidth, verticesHeight); @@ -700,17 +711,22 @@ TEST_CASE("Test converting quantized mesh to gltf with skirt") { REQUIRE(loadResult->model != std::nullopt); // make sure the gltf is the grid - const tinygltf::Model& model = *loadResult->model; - const tinygltf::Mesh& mesh = model.meshes.front(); - const tinygltf::Primitive& primitive = mesh.primitives.front(); + const CesiumGltf::Model& model = *loadResult->model; + const CesiumGltf::Mesh& mesh = model.meshes.front(); + const CesiumGltf::MeshPrimitive& primitive = mesh.primitives.front(); // make sure mesh contains grid mesh and skirts at the end - GltfAccessor indices(model, static_cast(primitive.indices)); - GltfAccessor positions(model, static_cast(primitive.attributes.at("POSITION"))); + AccessorView indices(model, primitive.indices); + CHECK(indices.status() == AccessorViewStatus::Valid); + AccessorView positions(model, primitive.attributes.at("POSITION")); + CHECK(positions.status() == AccessorViewStatus::Valid); + checkGridMesh(quantizedMesh, indices, positions, tilingScheme, ellipsoid, tileRectangle, verticesWidth, verticesHeight); // check normal - GltfAccessor normals(model, static_cast(primitive.attributes.at("NORMAL"))); + AccessorView normals(model, primitive.attributes.at("NORMAL")); + CHECK(normals.status() == AccessorViewStatus::Valid); + Cartographic center = boundingVolume.getRectangle().computeCenter(); glm::vec3 geodeticNormal = static_cast(ellipsoid.geodeticSurfaceNormal(center)); checkGeneratedGridNormal(quantizedMesh, normals, positions, indices, geodeticNormal, verticesWidth, verticesHeight); @@ -764,9 +780,9 @@ TEST_CASE("Test converting quantized mesh to gltf with skirt") { REQUIRE(loadResult->model != std::nullopt); // make sure the gltf has normals - const tinygltf::Model& model = *loadResult->model; - const tinygltf::Mesh& mesh = model.meshes.front(); - const tinygltf::Primitive& primitive = mesh.primitives.front(); + const CesiumGltf::Model& model = *loadResult->model; + const CesiumGltf::Mesh& mesh = model.meshes.front(); + const CesiumGltf::MeshPrimitive& primitive = mesh.primitives.front(); size_t westIndicesCount = quantizedMesh.vertexData.westIndices.size(); size_t southIndicesCount = quantizedMesh.vertexData.southIndices.size(); @@ -774,9 +790,11 @@ TEST_CASE("Test converting quantized mesh to gltf with skirt") { size_t northIndicesCount = quantizedMesh.vertexData.northIndices.size(); size_t totalSkirtVerticesCount = westIndicesCount + southIndicesCount + eastIndicesCount + northIndicesCount; - GltfAccessor normals(model, static_cast(primitive.attributes.at("NORMAL"))); - REQUIRE(normals.size() == (verticesWidth * verticesHeight + totalSkirtVerticesCount)); - for (size_t i = 0; i < normals.size(); ++i) { + AccessorView normals(model, primitive.attributes.at("NORMAL")); + CHECK(normals.status() == AccessorViewStatus::Valid); + + REQUIRE(static_cast(normals.size()) == (verticesWidth * verticesHeight + totalSkirtVerticesCount)); + for (int64_t i = 0; i < normals.size(); ++i) { REQUIRE(Math::equalsEpsilon(normals[i].x, normal.x, Math::EPSILON2)); REQUIRE(Math::equalsEpsilon(normals[i].y, normal.y, Math::EPSILON2)); REQUIRE(Math::equalsEpsilon(normals[i].z, normal.z, Math::EPSILON2)); diff --git a/Cesium3DTiles/test/TestSkirtMeshMetadata.cpp b/Cesium3DTiles/test/TestSkirtMeshMetadata.cpp index db0be0883..34cfe8bae 100644 --- a/Cesium3DTiles/test/TestSkirtMeshMetadata.cpp +++ b/Cesium3DTiles/test/TestSkirtMeshMetadata.cpp @@ -4,6 +4,7 @@ using namespace Cesium3DTiles; using namespace CesiumUtility; +using namespace CesiumGltf; TEST_CASE("Test converting skirt mesh metadata to gltf extras") { SkirtMeshMetadata skirtMeshMetadata; @@ -15,48 +16,48 @@ TEST_CASE("Test converting skirt mesh metadata to gltf extras") { skirtMeshMetadata.skirtEastHeight = 24.2; skirtMeshMetadata.skirtNorthHeight = 10.0; - tinygltf::Value extras = SkirtMeshMetadata::createGltfExtras(skirtMeshMetadata); - REQUIRE(extras.IsObject()); - REQUIRE(extras.Has("skirtMeshMetadata")); + JsonValue::Object extras = SkirtMeshMetadata::createGltfExtras(skirtMeshMetadata); + REQUIRE(extras.find("skirtMeshMetadata") != extras.end()); - tinygltf::Value gltfSkirt = extras.Get("skirtMeshMetadata"); - tinygltf::Value noSkirtRange = gltfSkirt.Get("noSkirtRange"); - REQUIRE(noSkirtRange.Get(0).GetNumberAsInt() == 0); - REQUIRE(noSkirtRange.Get(1).GetNumberAsInt() == 12); + JsonValue& gltfSkirt = extras["skirtMeshMetadata"]; + JsonValue::Array* pNoSkirtRange = gltfSkirt.getValueForKey("noSkirtRange"); + REQUIRE(pNoSkirtRange != nullptr); + REQUIRE((*pNoSkirtRange)[0].getNumber(-1.0) == 0.0); + REQUIRE((*pNoSkirtRange)[1].getNumber(-1.0) == 12.0); - tinygltf::Value meshCenter = gltfSkirt.Get("meshCenter"); - REQUIRE(Math::equalsEpsilon(meshCenter.Get(0).GetNumberAsDouble(), skirtMeshMetadata.meshCenter.x, Math::EPSILON7)); - REQUIRE(Math::equalsEpsilon(meshCenter.Get(1).GetNumberAsDouble(), skirtMeshMetadata.meshCenter.y, Math::EPSILON7)); - REQUIRE(Math::equalsEpsilon(meshCenter.Get(2).GetNumberAsDouble(), skirtMeshMetadata.meshCenter.z, Math::EPSILON7)); + JsonValue::Array* pMeshCenter = gltfSkirt.getValueForKey("meshCenter"); + REQUIRE(Math::equalsEpsilon((*pMeshCenter)[0].getNumber(0.0), skirtMeshMetadata.meshCenter.x, Math::EPSILON7)); + REQUIRE(Math::equalsEpsilon((*pMeshCenter)[1].getNumber(0.0), skirtMeshMetadata.meshCenter.y, Math::EPSILON7)); + REQUIRE(Math::equalsEpsilon((*pMeshCenter)[2].getNumber(0.0), skirtMeshMetadata.meshCenter.z, Math::EPSILON7)); - tinygltf::Value skirtWestHeight = gltfSkirt.Get("skirtWestHeight"); - REQUIRE(Math::equalsEpsilon(skirtWestHeight.GetNumberAsDouble(), skirtMeshMetadata.skirtWestHeight, Math::EPSILON7)); + double* pSkirtWestHeight = gltfSkirt.getValueForKey("skirtWestHeight"); + REQUIRE(Math::equalsEpsilon(*pSkirtWestHeight, skirtMeshMetadata.skirtWestHeight, Math::EPSILON7)); - tinygltf::Value skirtSouthHeight = gltfSkirt.Get("skirtSouthHeight"); - REQUIRE(Math::equalsEpsilon(skirtSouthHeight.GetNumberAsDouble(), skirtMeshMetadata.skirtSouthHeight, Math::EPSILON7)); + double* pSkirtSouthHeight = gltfSkirt.getValueForKey("skirtSouthHeight"); + REQUIRE(Math::equalsEpsilon(*pSkirtSouthHeight, skirtMeshMetadata.skirtSouthHeight, Math::EPSILON7)); - tinygltf::Value skirtEastHeight = gltfSkirt.Get("skirtEastHeight"); - REQUIRE(Math::equalsEpsilon(skirtEastHeight.GetNumberAsDouble(), skirtMeshMetadata.skirtEastHeight, Math::EPSILON7)); + double* pSkirtEastHeight = gltfSkirt.getValueForKey("skirtEastHeight"); + REQUIRE(Math::equalsEpsilon(*pSkirtEastHeight, skirtMeshMetadata.skirtEastHeight, Math::EPSILON7)); - tinygltf::Value skirtNorthHeight = gltfSkirt.Get("skirtNorthHeight"); - REQUIRE(Math::equalsEpsilon(skirtNorthHeight.GetNumberAsDouble(), skirtMeshMetadata.skirtNorthHeight, Math::EPSILON7)); + double* pSkirtNorthHeight = gltfSkirt.getValueForKey("skirtNorthHeight"); + REQUIRE(Math::equalsEpsilon(*pSkirtNorthHeight, skirtMeshMetadata.skirtNorthHeight, Math::EPSILON7)); } TEST_CASE("Test converting gltf extras to skirt mesh metadata") { // mock gltf extras for skirt mesh metadata - tinygltf::Value::Object gltfSkirtMeshMetadata; - gltfSkirtMeshMetadata.insert({ "noSkirtRange", tinygltf::Value(tinygltf::Value::Array({ - tinygltf::Value(0), tinygltf::Value(12)})) }); - gltfSkirtMeshMetadata.insert({ "meshCenter", tinygltf::Value(tinygltf::Value::Array({ - tinygltf::Value(1.0), tinygltf::Value(2.0), tinygltf::Value(3.0)})) }); - gltfSkirtMeshMetadata.insert({ "skirtWestHeight", tinygltf::Value(12.4) }); - gltfSkirtMeshMetadata.insert({ "skirtSouthHeight", tinygltf::Value(10.0) }); - gltfSkirtMeshMetadata.insert({ "skirtEastHeight", tinygltf::Value(2.4) }); - gltfSkirtMeshMetadata.insert({ "skirtNorthHeight", tinygltf::Value(1.4) }); + JsonValue::Object gltfSkirtMeshMetadata = { + { "noSkirtRange", JsonValue::Array { 0, 12 } }, + { "meshCenter", JsonValue::Array { 1.0, 2.0, 3.0 } }, + { "skirtWestHeight", 12.4 }, + { "skirtSouthHeight", 10.0 }, + { "skirtEastHeight", 2.4 }, + { "skirtNorthHeight", 1.4 } + }; SECTION("Gltf Extras has correct format") { - tinygltf::Value extras = tinygltf::Value( - tinygltf::Value::Object{ {"skirtMeshMetadata", tinygltf::Value(gltfSkirtMeshMetadata)} }); + JsonValue::Object extras = { + { "skirtMeshMetadata", gltfSkirtMeshMetadata } + }; SkirtMeshMetadata skirtMeshMetadata = *SkirtMeshMetadata::parseFromGltfExtras(extras); @@ -75,34 +76,39 @@ TEST_CASE("Test converting gltf extras to skirt mesh metadata") { SECTION("missing noSkirtRange field") { gltfSkirtMeshMetadata.erase("noSkirtRange"); - tinygltf::Value extras = tinygltf::Value( - tinygltf::Value::Object{ {"skirtMeshMetadata", tinygltf::Value(gltfSkirtMeshMetadata)} }); + JsonValue::Object extras = { + { "skirtMeshMetadata", gltfSkirtMeshMetadata } + }; REQUIRE(SkirtMeshMetadata::parseFromGltfExtras(extras) == std::nullopt); } SECTION("noSkirtRange field has wrong type") { - gltfSkirtMeshMetadata["noSkirtRange"] = tinygltf::Value(12); + gltfSkirtMeshMetadata["noSkirtRange"] = 12; - tinygltf::Value extras = tinygltf::Value( - tinygltf::Value::Object{ {"skirtMeshMetadata", tinygltf::Value(gltfSkirtMeshMetadata)} }); + JsonValue::Object extras = { + { "skirtMeshMetadata", gltfSkirtMeshMetadata } + }; REQUIRE(SkirtMeshMetadata::parseFromGltfExtras(extras) == std::nullopt); } SECTION("noSkirtRange field has only one element array") { - gltfSkirtMeshMetadata["noSkirtRange"] = tinygltf::Value(tinygltf::Value::Array({tinygltf::Value(0)})); - tinygltf::Value extras = tinygltf::Value( - tinygltf::Value::Object{ {"skirtMeshMetadata", tinygltf::Value(gltfSkirtMeshMetadata)} }); + gltfSkirtMeshMetadata["noSkirtRange"] = JsonValue::Array { 0 }; + + JsonValue::Object extras = { + { "skirtMeshMetadata", gltfSkirtMeshMetadata } + }; REQUIRE(SkirtMeshMetadata::parseFromGltfExtras(extras) == std::nullopt); } SECTION("noSkirtRange field has two elements array but not integer") { - gltfSkirtMeshMetadata["noSkirtRange"] = tinygltf::Value( - tinygltf::Value::Array({tinygltf::Value(std::string("first")), tinygltf::Value(std::string("second"))})); - tinygltf::Value extras = tinygltf::Value( - tinygltf::Value::Object{ {"skirtMeshMetadata", tinygltf::Value(gltfSkirtMeshMetadata)} }); + gltfSkirtMeshMetadata["noSkirtRange"] = JsonValue::Array { "first", "second" }; + + JsonValue::Object extras = { + { "skirtMeshMetadata", gltfSkirtMeshMetadata } + }; REQUIRE(SkirtMeshMetadata::parseFromGltfExtras(extras) == std::nullopt); } @@ -112,37 +118,39 @@ TEST_CASE("Test converting gltf extras to skirt mesh metadata") { SECTION("missing meshCenter field") { gltfSkirtMeshMetadata.erase("meshCenter"); - tinygltf::Value extras = tinygltf::Value( - tinygltf::Value::Object{ {"skirtMeshMetadata", tinygltf::Value(gltfSkirtMeshMetadata)} }); + JsonValue::Object extras = { + { "skirtMeshMetadata", gltfSkirtMeshMetadata } + }; REQUIRE(SkirtMeshMetadata::parseFromGltfExtras(extras) == std::nullopt); } SECTION("meshCenter field has wrong type") { - gltfSkirtMeshMetadata["meshCenter"] = tinygltf::Value(12); + gltfSkirtMeshMetadata["meshCenter"] = 12; - tinygltf::Value extras = tinygltf::Value( - tinygltf::Value::Object{ {"skirtMeshMetadata", tinygltf::Value(gltfSkirtMeshMetadata)} }); + JsonValue::Object extras = { + { "skirtMeshMetadata", gltfSkirtMeshMetadata } + }; REQUIRE(SkirtMeshMetadata::parseFromGltfExtras(extras) == std::nullopt); } SECTION("meshCenter field is 2 elements array") { - gltfSkirtMeshMetadata["meshCenter"] = tinygltf::Value( - tinygltf::Value::Array({tinygltf::Value(1.0), tinygltf::Value(2.0)})); + gltfSkirtMeshMetadata["meshCenter"] = JsonValue::Array { 1.0, 2.0 }; - tinygltf::Value extras = tinygltf::Value( - tinygltf::Value::Object{ {"skirtMeshMetadata", tinygltf::Value(gltfSkirtMeshMetadata)} }); + JsonValue::Object extras = { + { "skirtMeshMetadata", gltfSkirtMeshMetadata } + }; REQUIRE(SkirtMeshMetadata::parseFromGltfExtras(extras) == std::nullopt); } SECTION("meshCenter field is 3 elements array but wrong type") { - gltfSkirtMeshMetadata["meshCenter"] = tinygltf::Value( - tinygltf::Value::Array({tinygltf::Value(1.0), tinygltf::Value(2.0), tinygltf::Value(std::string("third"))})); + gltfSkirtMeshMetadata["meshCenter"] = JsonValue::Array { 1.0, 2.0, "third" }; - tinygltf::Value extras = tinygltf::Value( - tinygltf::Value::Object{ {"skirtMeshMetadata", tinygltf::Value(gltfSkirtMeshMetadata)} }); + JsonValue::Object extras = { + { "skirtMeshMetadata", gltfSkirtMeshMetadata } + }; REQUIRE(SkirtMeshMetadata::parseFromGltfExtras(extras) == std::nullopt); } @@ -152,17 +160,19 @@ TEST_CASE("Test converting gltf extras to skirt mesh metadata") { SECTION("missing skirtWestHeight field") { gltfSkirtMeshMetadata.erase("skirtWestHeight"); - tinygltf::Value extras = tinygltf::Value( - tinygltf::Value::Object{ {"skirtMeshMetadata", tinygltf::Value(gltfSkirtMeshMetadata)} }); + JsonValue::Object extras = { + { "skirtMeshMetadata", gltfSkirtMeshMetadata } + }; REQUIRE(SkirtMeshMetadata::parseFromGltfExtras(extras) == std::nullopt); } SECTION("skirtWestHeight field has wrong type") { - gltfSkirtMeshMetadata["skirtWestHeight"] = tinygltf::Value(std::string("string")); + gltfSkirtMeshMetadata["skirtWestHeight"] = "string"; - tinygltf::Value extras = tinygltf::Value( - tinygltf::Value::Object{ {"skirtMeshMetadata", tinygltf::Value(gltfSkirtMeshMetadata)} }); + JsonValue::Object extras = { + { "skirtMeshMetadata", gltfSkirtMeshMetadata } + }; REQUIRE(SkirtMeshMetadata::parseFromGltfExtras(extras) == std::nullopt); } @@ -172,17 +182,19 @@ TEST_CASE("Test converting gltf extras to skirt mesh metadata") { SECTION("missing skirtSouthHeight field") { gltfSkirtMeshMetadata.erase("skirtSouthHeight"); - tinygltf::Value extras = tinygltf::Value( - tinygltf::Value::Object{ {"skirtMeshMetadata", tinygltf::Value(gltfSkirtMeshMetadata)} }); + JsonValue::Object extras = { + { "skirtMeshMetadata", gltfSkirtMeshMetadata } + }; REQUIRE(SkirtMeshMetadata::parseFromGltfExtras(extras) == std::nullopt); } SECTION("skirtSouthHeight field has wrong type") { - gltfSkirtMeshMetadata["skirtSouthHeight"] = tinygltf::Value(std::string("string")); + gltfSkirtMeshMetadata["skirtSouthHeight"] = "string"; - tinygltf::Value extras = tinygltf::Value( - tinygltf::Value::Object{ {"skirtMeshMetadata", tinygltf::Value(gltfSkirtMeshMetadata)} }); + JsonValue::Object extras = { + { "skirtMeshMetadata", gltfSkirtMeshMetadata } + }; REQUIRE(SkirtMeshMetadata::parseFromGltfExtras(extras) == std::nullopt); } @@ -192,17 +204,19 @@ TEST_CASE("Test converting gltf extras to skirt mesh metadata") { SECTION("missing skirtEastHeight field") { gltfSkirtMeshMetadata.erase("skirtEastHeight"); - tinygltf::Value extras = tinygltf::Value( - tinygltf::Value::Object{ {"skirtMeshMetadata", tinygltf::Value(gltfSkirtMeshMetadata)} }); + JsonValue::Object extras = { + { "skirtMeshMetadata", gltfSkirtMeshMetadata } + }; REQUIRE(SkirtMeshMetadata::parseFromGltfExtras(extras) == std::nullopt); } SECTION("skirtEastHeight field has wrong type") { - gltfSkirtMeshMetadata["skirtEastHeight"] = tinygltf::Value(std::string("string")); + gltfSkirtMeshMetadata["skirtEastHeight"] = "string"; - tinygltf::Value extras = tinygltf::Value( - tinygltf::Value::Object{ {"skirtMeshMetadata", tinygltf::Value(gltfSkirtMeshMetadata)} }); + JsonValue::Object extras = { + { "skirtMeshMetadata", gltfSkirtMeshMetadata } + }; REQUIRE(SkirtMeshMetadata::parseFromGltfExtras(extras) == std::nullopt); } @@ -212,17 +226,19 @@ TEST_CASE("Test converting gltf extras to skirt mesh metadata") { SECTION("missing skirtNorthHeight field") { gltfSkirtMeshMetadata.erase("skirtNorthHeight"); - tinygltf::Value extras = tinygltf::Value( - tinygltf::Value::Object{ {"skirtMeshMetadata", tinygltf::Value(gltfSkirtMeshMetadata)} }); + JsonValue::Object extras = { + { "skirtMeshMetadata", gltfSkirtMeshMetadata } + }; REQUIRE(SkirtMeshMetadata::parseFromGltfExtras(extras) == std::nullopt); } SECTION("skirtNorthHeight field has wrong type") { - gltfSkirtMeshMetadata["skirtNorthHeight"] = tinygltf::Value(std::string("string")); + gltfSkirtMeshMetadata["skirtNorthHeight"] = "string"; - tinygltf::Value extras = tinygltf::Value( - tinygltf::Value::Object{ {"skirtMeshMetadata", tinygltf::Value(gltfSkirtMeshMetadata)} }); + JsonValue::Object extras = { + { "skirtMeshMetadata", gltfSkirtMeshMetadata } + }; REQUIRE(SkirtMeshMetadata::parseFromGltfExtras(extras) == std::nullopt); } diff --git a/Cesium3DTiles/test/TestUpsampleGltfForRasterOverlay.cpp b/Cesium3DTiles/test/TestUpsampleGltfForRasterOverlay.cpp index 42fb88253..3741aed14 100644 --- a/Cesium3DTiles/test/TestUpsampleGltfForRasterOverlay.cpp +++ b/Cesium3DTiles/test/TestUpsampleGltfForRasterOverlay.cpp @@ -1,17 +1,19 @@ #include "catch2/catch.hpp" #include "Cesium3DTiles/Gltf.h" -#include "Cesium3DTiles/GltfAccessor.h" -#include "CesiumUtility/Math.h" #include "CesiumGeospatial/Cartographic.h" #include "CesiumGeospatial/Ellipsoid.h" +#include "CesiumGltf/AccessorView.h" +#include "CesiumUtility/Math.h" #include "SkirtMeshMetadata.h" #include "upsampleGltfForRasterOverlays.h" -#include "glm/trigonometric.hpp" +#include +#include #include using namespace Cesium3DTiles; using namespace CesiumUtility; using namespace CesiumGeospatial; +using namespace CesiumGltf; static void checkSkirt(const Ellipsoid &ellipsoid, const glm::vec3 &edgeUpsampledPosition, const glm::vec3 &skirtUpsampledPosition, glm::dvec3 center, double skirtHeight) { glm::dvec3 edgePosition = static_cast(edgeUpsampledPosition) + center; @@ -50,83 +52,80 @@ TEST_CASE("Test upsample tile without skirts") { uint32_t uvsBufferSize = static_cast(uvs.size() * sizeof(glm::vec2)); uint32_t indicesBufferSize = static_cast(indices.size() * sizeof(uint16_t)); - tinygltf::Model model; + Model model; // create buffer model.buffers.emplace_back(); - tinygltf::Buffer &buffer = model.buffers.back(); - buffer.data.resize(positionsBufferSize + uvsBufferSize + indicesBufferSize); - std::memcpy(buffer.data.data(), positions.data(), positionsBufferSize); - std::memcpy(buffer.data.data() + positionsBufferSize, uvs.data(), uvsBufferSize); - std::memcpy(buffer.data.data() + positionsBufferSize + uvsBufferSize, indices.data(), indicesBufferSize); + Buffer &buffer = model.buffers.back(); + buffer.cesium.data.resize(positionsBufferSize + uvsBufferSize + indicesBufferSize); + std::memcpy(buffer.cesium.data.data(), positions.data(), positionsBufferSize); + std::memcpy(buffer.cesium.data.data() + positionsBufferSize, uvs.data(), uvsBufferSize); + std::memcpy(buffer.cesium.data.data() + positionsBufferSize + uvsBufferSize, indices.data(), indicesBufferSize); // create position model.bufferViews.emplace_back(); - tinygltf::BufferView &positionBufferView = model.bufferViews.emplace_back(); + BufferView &positionBufferView = model.bufferViews.emplace_back(); positionBufferView.buffer = static_cast(model.buffers.size() - 1); positionBufferView.byteOffset = 0; positionBufferView.byteLength = positionsBufferSize; - positionBufferView.byteStride = 0; model.accessors.emplace_back(); - tinygltf::Accessor &positionAccessor = model.accessors.back(); - positionAccessor.bufferView = static_cast(model.bufferViews.size() - 1); + Accessor &positionAccessor = model.accessors.back(); + positionAccessor.bufferView = static_cast(model.bufferViews.size() - 1); positionAccessor.byteOffset = 0; - positionAccessor.count = positions.size(); - positionAccessor.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT; - positionAccessor.type = TINYGLTF_TYPE_VEC3; + positionAccessor.count = static_cast(positions.size()); + positionAccessor.componentType = Accessor::ComponentType::FLOAT; + positionAccessor.type = Accessor::Type::VEC3; - int positionAccessorIdx = static_cast(model.accessors.size() - 1); + int32_t positionAccessorIdx = static_cast(model.accessors.size() - 1); // create uv model.bufferViews.emplace_back(); - tinygltf::BufferView &uvBufferView = model.bufferViews.emplace_back(); - uvBufferView.buffer = static_cast(model.buffers.size() - 1); + BufferView &uvBufferView = model.bufferViews.emplace_back(); + uvBufferView.buffer = static_cast(model.buffers.size() - 1); uvBufferView.byteOffset = positionsBufferSize; uvBufferView.byteLength = uvsBufferSize; - uvBufferView.byteStride = 0; model.accessors.emplace_back(); - tinygltf::Accessor &uvAccessor = model.accessors.back(); - uvAccessor.bufferView = static_cast(model.bufferViews.size() - 1); + Accessor &uvAccessor = model.accessors.back(); + uvAccessor.bufferView = static_cast(model.bufferViews.size() - 1); uvAccessor.byteOffset = 0; - uvAccessor.count = uvs.size(); - uvAccessor.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT; - uvAccessor.type = TINYGLTF_TYPE_VEC2; + uvAccessor.count = static_cast(uvs.size()); + uvAccessor.componentType = Accessor::ComponentType::FLOAT; + uvAccessor.type = Accessor::Type::VEC2; - int uvAccessorIdx = static_cast(model.accessors.size() - 1); + int32_t uvAccessorIdx = static_cast(model.accessors.size() - 1); // create indices model.bufferViews.emplace_back(); - tinygltf::BufferView &indicesBufferView = model.bufferViews.emplace_back(); + BufferView &indicesBufferView = model.bufferViews.emplace_back(); indicesBufferView.buffer = static_cast(model.buffers.size() - 1); indicesBufferView.byteOffset = positionsBufferSize + uvsBufferSize; indicesBufferView.byteLength = indicesBufferSize; - indicesBufferView.byteStride = 0; model.accessors.emplace_back(); - tinygltf::Accessor &indicesAccessor = model.accessors.back(); + Accessor &indicesAccessor = model.accessors.back(); indicesAccessor.bufferView = static_cast(model.bufferViews.size() - 1); indicesAccessor.byteOffset = 0; - indicesAccessor.count = indices.size(); - indicesAccessor.componentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT; - indicesAccessor.type = TINYGLTF_TYPE_SCALAR; + indicesAccessor.count = static_cast(indices.size()); + indicesAccessor.componentType = Accessor::ComponentType::UNSIGNED_SHORT; + indicesAccessor.type = Accessor::Type::SCALAR; int indicesAccessorIdx = static_cast(model.accessors.size() - 1); model.meshes.emplace_back(); - tinygltf::Mesh &mesh = model.meshes.back(); + Mesh &mesh = model.meshes.back(); mesh.primitives.emplace_back(); - tinygltf::Primitive &primitive = mesh.primitives.back(); - primitive.mode = TINYGLTF_MODE_TRIANGLES; + MeshPrimitive &primitive = mesh.primitives.back(); + primitive.mode = MeshPrimitive::Mode::TRIANGLES; primitive.attributes["_CESIUMOVERLAY_0"] = uvAccessorIdx; primitive.attributes["POSITION"] = positionAccessorIdx; primitive.indices = indicesAccessorIdx; // create node and update bounding volume model.nodes.emplace_back(); - tinygltf::Node& node = model.nodes[0]; + Node& node = model.nodes[0]; node.mesh = static_cast(model.meshes.size() - 1); node.matrix = { 1.0, 0.0, 0.0, 0.0, @@ -136,18 +135,18 @@ TEST_CASE("Test upsample tile without skirts") { }; SECTION("Upsample bottom left child") { - tinygltf::Model upsampledModel = upsampleGltfForRasterOverlays(model, CesiumGeometry::QuadtreeChild::LowerLeft); + Model upsampledModel = upsampleGltfForRasterOverlays(model, CesiumGeometry::QuadtreeChild::LowerLeft); REQUIRE(upsampledModel.meshes.size() == 1); - const tinygltf::Mesh& upsampledMesh = upsampledModel.meshes.back(); + const Mesh& upsampledMesh = upsampledModel.meshes.back(); REQUIRE(upsampledMesh.primitives.size() == 1); - const tinygltf::Primitive& upsampledPrimitive = upsampledMesh.primitives.back(); + const MeshPrimitive& upsampledPrimitive = upsampledMesh.primitives.back(); REQUIRE(upsampledPrimitive.indices >= 0); REQUIRE(upsampledPrimitive.attributes.find("POSITION") != upsampledPrimitive.attributes.end()); - GltfAccessor upsampledPosition(upsampledModel, static_cast(upsampledPrimitive.attributes.at("POSITION"))); - GltfAccessor upsampledIndices(upsampledModel, static_cast(upsampledPrimitive.indices)); + AccessorView upsampledPosition(upsampledModel, upsampledPrimitive.attributes.at("POSITION")); + AccessorView upsampledIndices(upsampledModel, upsampledPrimitive.indices); glm::vec3 p0 = upsampledPosition[0]; REQUIRE(glm::epsilonEqual(p0, positions[0], glm::vec3(static_cast(Math::EPSILON7))) == glm::bvec3(true)); @@ -172,18 +171,18 @@ TEST_CASE("Test upsample tile without skirts") { } SECTION("Upsample upper left child") { - tinygltf::Model upsampledModel = upsampleGltfForRasterOverlays(model, CesiumGeometry::QuadtreeChild::UpperLeft); + Model upsampledModel = upsampleGltfForRasterOverlays(model, CesiumGeometry::QuadtreeChild::UpperLeft); REQUIRE(upsampledModel.meshes.size() == 1); - const tinygltf::Mesh& upsampledMesh = upsampledModel.meshes.back(); + const Mesh& upsampledMesh = upsampledModel.meshes.back(); REQUIRE(upsampledMesh.primitives.size() == 1); - const tinygltf::Primitive& upsampledPrimitive = upsampledMesh.primitives.back(); + const MeshPrimitive& upsampledPrimitive = upsampledMesh.primitives.back(); REQUIRE(upsampledPrimitive.indices >= 0); REQUIRE(upsampledPrimitive.attributes.find("POSITION") != upsampledPrimitive.attributes.end()); - GltfAccessor upsampledPosition(upsampledModel, static_cast(upsampledPrimitive.attributes.at("POSITION"))); - GltfAccessor upsampledIndices(upsampledModel, static_cast(upsampledPrimitive.indices)); + AccessorView upsampledPosition(upsampledModel, upsampledPrimitive.attributes.at("POSITION")); + AccessorView upsampledIndices(upsampledModel, upsampledPrimitive.indices); glm::vec3 p0 = upsampledPosition[0]; REQUIRE(glm::epsilonEqual(p0, positions[1], glm::vec3(static_cast(Math::EPSILON7))) == glm::bvec3(true)); @@ -208,18 +207,18 @@ TEST_CASE("Test upsample tile without skirts") { } SECTION("Upsample upper right child") { - tinygltf::Model upsampledModel = upsampleGltfForRasterOverlays(model, CesiumGeometry::QuadtreeChild::UpperRight); + Model upsampledModel = upsampleGltfForRasterOverlays(model, CesiumGeometry::QuadtreeChild::UpperRight); REQUIRE(upsampledModel.meshes.size() == 1); - const tinygltf::Mesh& upsampledMesh = upsampledModel.meshes.back(); + const Mesh& upsampledMesh = upsampledModel.meshes.back(); REQUIRE(upsampledMesh.primitives.size() == 1); - const tinygltf::Primitive& upsampledPrimitive = upsampledMesh.primitives.back(); + const MeshPrimitive& upsampledPrimitive = upsampledMesh.primitives.back(); REQUIRE(upsampledPrimitive.indices >= 0); REQUIRE(upsampledPrimitive.attributes.find("POSITION") != upsampledPrimitive.attributes.end()); - GltfAccessor upsampledPosition(upsampledModel, static_cast(upsampledPrimitive.attributes.at("POSITION"))); - GltfAccessor upsampledIndices(upsampledModel, static_cast(upsampledPrimitive.indices)); + AccessorView upsampledPosition(upsampledModel, upsampledPrimitive.attributes.at("POSITION")); + AccessorView upsampledIndices(upsampledModel, upsampledPrimitive.indices); glm::vec3 p0 = upsampledPosition[0]; REQUIRE(glm::epsilonEqual(p0, positions[3], glm::vec3(static_cast(Math::EPSILON7))) == glm::bvec3(true)); @@ -244,18 +243,18 @@ TEST_CASE("Test upsample tile without skirts") { } SECTION("Upsample bottom right child") { - tinygltf::Model upsampledModel = upsampleGltfForRasterOverlays(model, CesiumGeometry::QuadtreeChild::LowerRight); + Model upsampledModel = upsampleGltfForRasterOverlays(model, CesiumGeometry::QuadtreeChild::LowerRight); REQUIRE(upsampledModel.meshes.size() == 1); - const tinygltf::Mesh& upsampledMesh = upsampledModel.meshes.back(); + const Mesh& upsampledMesh = upsampledModel.meshes.back(); REQUIRE(upsampledMesh.primitives.size() == 1); - const tinygltf::Primitive& upsampledPrimitive = upsampledMesh.primitives.back(); + const MeshPrimitive& upsampledPrimitive = upsampledMesh.primitives.back(); REQUIRE(upsampledPrimitive.indices >= 0); REQUIRE(upsampledPrimitive.attributes.find("POSITION") != upsampledPrimitive.attributes.end()); - GltfAccessor upsampledPosition(upsampledModel, static_cast(upsampledPrimitive.attributes.at("POSITION"))); - GltfAccessor upsampledIndices(upsampledModel, static_cast(upsampledPrimitive.indices)); + AccessorView upsampledPosition(upsampledModel, upsampledPrimitive.attributes.at("POSITION")); + AccessorView upsampledIndices(upsampledModel, upsampledPrimitive.indices); glm::vec3 p0 = upsampledPosition[0]; REQUIRE(glm::epsilonEqual(p0, positions[2], glm::vec3(static_cast(Math::EPSILON7))) == glm::bvec3(true)); @@ -294,18 +293,18 @@ TEST_CASE("Test upsample tile without skirts") { primitive.extras = SkirtMeshMetadata::createGltfExtras(skirtMeshMetadata); SECTION("Check bottom left skirt") { - tinygltf::Model upsampledModel = upsampleGltfForRasterOverlays(model, CesiumGeometry::QuadtreeChild::LowerLeft); + Model upsampledModel = upsampleGltfForRasterOverlays(model, CesiumGeometry::QuadtreeChild::LowerLeft); REQUIRE(upsampledModel.meshes.size() == 1); - const tinygltf::Mesh& upsampledMesh = upsampledModel.meshes.back(); + const Mesh& upsampledMesh = upsampledModel.meshes.back(); REQUIRE(upsampledMesh.primitives.size() == 1); - const tinygltf::Primitive& upsampledPrimitive = upsampledMesh.primitives.back(); + const MeshPrimitive& upsampledPrimitive = upsampledMesh.primitives.back(); REQUIRE(upsampledPrimitive.indices >= 0); REQUIRE(upsampledPrimitive.attributes.find("POSITION") != upsampledPrimitive.attributes.end()); - GltfAccessor upsampledPosition(upsampledModel, static_cast(upsampledPrimitive.attributes.at("POSITION"))); - GltfAccessor upsampledIndices(upsampledModel, static_cast(upsampledPrimitive.indices)); + AccessorView upsampledPosition(upsampledModel, upsampledPrimitive.attributes.at("POSITION")); + AccessorView upsampledIndices(upsampledModel, upsampledPrimitive.indices); // check west edge checkSkirt(ellipsoid, upsampledPosition[0], upsampledPosition[7], center, skirtHeight); @@ -329,18 +328,18 @@ TEST_CASE("Test upsample tile without skirts") { } SECTION("Check upper left skirt") { - tinygltf::Model upsampledModel = upsampleGltfForRasterOverlays(model, CesiumGeometry::QuadtreeChild::UpperLeft); + Model upsampledModel = upsampleGltfForRasterOverlays(model, CesiumGeometry::QuadtreeChild::UpperLeft); REQUIRE(upsampledModel.meshes.size() == 1); - const tinygltf::Mesh& upsampledMesh = upsampledModel.meshes.back(); + const Mesh& upsampledMesh = upsampledModel.meshes.back(); REQUIRE(upsampledMesh.primitives.size() == 1); - const tinygltf::Primitive& upsampledPrimitive = upsampledMesh.primitives.back(); + const MeshPrimitive& upsampledPrimitive = upsampledMesh.primitives.back(); REQUIRE(upsampledPrimitive.indices >= 0); REQUIRE(upsampledPrimitive.attributes.find("POSITION") != upsampledPrimitive.attributes.end()); - GltfAccessor upsampledPosition(upsampledModel, static_cast(upsampledPrimitive.attributes.at("POSITION"))); - GltfAccessor upsampledIndices(upsampledModel, static_cast(upsampledPrimitive.indices)); + AccessorView upsampledPosition(upsampledModel, upsampledPrimitive.attributes.at("POSITION")); + AccessorView upsampledIndices(upsampledModel, upsampledPrimitive.indices); // check west edge checkSkirt(ellipsoid, upsampledPosition[1], upsampledPosition[7], center, skirtHeight); @@ -368,18 +367,18 @@ TEST_CASE("Test upsample tile without skirts") { } SECTION("Check upper right skirt") { - tinygltf::Model upsampledModel = upsampleGltfForRasterOverlays(model, CesiumGeometry::QuadtreeChild::UpperRight); + Model upsampledModel = upsampleGltfForRasterOverlays(model, CesiumGeometry::QuadtreeChild::UpperRight); REQUIRE(upsampledModel.meshes.size() == 1); - const tinygltf::Mesh& upsampledMesh = upsampledModel.meshes.back(); + const Mesh& upsampledMesh = upsampledModel.meshes.back(); REQUIRE(upsampledMesh.primitives.size() == 1); - const tinygltf::Primitive& upsampledPrimitive = upsampledMesh.primitives.back(); + const MeshPrimitive& upsampledPrimitive = upsampledMesh.primitives.back(); REQUIRE(upsampledPrimitive.indices >= 0); REQUIRE(upsampledPrimitive.attributes.find("POSITION") != upsampledPrimitive.attributes.end()); - GltfAccessor upsampledPosition(upsampledModel, static_cast(upsampledPrimitive.attributes.at("POSITION"))); - GltfAccessor upsampledIndices(upsampledModel, static_cast(upsampledPrimitive.indices)); + AccessorView upsampledPosition(upsampledModel, upsampledPrimitive.attributes.at("POSITION")); + AccessorView upsampledIndices(upsampledModel, upsampledPrimitive.indices); // check west edge checkSkirt(ellipsoid, upsampledPosition[5], upsampledPosition[7], center, skirtHeight * 0.5); @@ -403,18 +402,18 @@ TEST_CASE("Test upsample tile without skirts") { } SECTION("Check bottom right skirt") { - tinygltf::Model upsampledModel = upsampleGltfForRasterOverlays(model, CesiumGeometry::QuadtreeChild::LowerRight); + Model upsampledModel = upsampleGltfForRasterOverlays(model, CesiumGeometry::QuadtreeChild::LowerRight); REQUIRE(upsampledModel.meshes.size() == 1); - const tinygltf::Mesh& upsampledMesh = upsampledModel.meshes.back(); + const Mesh& upsampledMesh = upsampledModel.meshes.back(); REQUIRE(upsampledMesh.primitives.size() == 1); - const tinygltf::Primitive& upsampledPrimitive = upsampledMesh.primitives.back(); + const MeshPrimitive& upsampledPrimitive = upsampledMesh.primitives.back(); REQUIRE(upsampledPrimitive.indices >= 0); REQUIRE(upsampledPrimitive.attributes.find("POSITION") != upsampledPrimitive.attributes.end()); - GltfAccessor upsampledPosition(upsampledModel, static_cast(upsampledPrimitive.attributes.at("POSITION"))); - GltfAccessor upsampledIndices(upsampledModel, static_cast(upsampledPrimitive.indices)); + AccessorView upsampledPosition(upsampledModel, upsampledPrimitive.attributes.at("POSITION")); + AccessorView upsampledIndices(upsampledModel, upsampledPrimitive.indices); // check west edge checkSkirt(ellipsoid, upsampledPosition[2], upsampledPosition[7], center, skirtHeight * 0.5); diff --git a/CesiumGltf/CMakeLists.txt b/CesiumGltf/CMakeLists.txt new file mode 100644 index 000000000..6652cd3a0 --- /dev/null +++ b/CesiumGltf/CMakeLists.txt @@ -0,0 +1,28 @@ +add_library(CesiumGltf "") + +configure_cesium_library(CesiumGltf) + +file(GLOB SOURCES include/CesiumGltf/*.h src/*.cpp src/*.h) +file(GLOB PUBLIC_INCLUDES include/CesiumGltf/*.h) + +target_sources( + CesiumGltf + PRIVATE + ${SOURCES} + PUBLIC + ${PUBLIC_INCLUDES} +) + +target_include_directories( + CesiumGltf + SYSTEM PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/../extern/rapidjson/include + PUBLIC + include + PRIVATE + src + SYSTEM PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/../extern/cgltf +) + +target_link_libraries(CesiumGltf PUBLIC) diff --git a/CesiumGltf/include/CesiumGltf/Accessor.h b/CesiumGltf/include/CesiumGltf/Accessor.h new file mode 100644 index 000000000..11014fdfb --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/Accessor.h @@ -0,0 +1,82 @@ +#pragma once + +#include "CesiumGltf/AccessorSpec.h" +#include "CesiumGltf/Library.h" +#include + +namespace CesiumGltf { + struct Model; + + /** @copydoc AccessorSpec */ + struct CESIUMGLTF_API Accessor final : public AccessorSpec { + /** + * @brief Computes the number of components for a given accessor type. + * + * For example `CesiumGltf::Accessor::Type::SCALAR` has 1 component while + * `CesiumGltf::Accessor::Type::VEC4` has 4 components. + * + * @param type The accessor type. + * @return The number of components. Returns 0 if {@link Accessor::type} is not a valid enumeration value. + */ + static int8_t computeNumberOfComponents(CesiumGltf::Accessor::Type type); + + /** + * @brief Computes the number of bytes for a given accessor component type. + * + * For example `CesiumGltf::Accessor::ComponentType::UNSIGNED_SHORT` is 2 bytes while + * `CesiumGltf::Accessor::ComponentType::FLOAT` is 4 bytes. + * + * @param componentType The accessor component type. + * @return The number of bytes for the component type. Returns 0 if {@link Accessor::componentType} is not a valid enumation value. + */ + static int8_t computeByteSizeOfComponent(CesiumGltf::Accessor::ComponentType componentType); + + /** + * @brief Computes the number of components for this accessor. + * + * For example if this accessor's {@link Accessor::type} is `CesiumGltf::Accessor::Type::SCALAR`, then it has 1 component, + * while if it's `CesiumGltf::Accessor::Type::VEC4` it has 4 components. + * + * @return The number of components in this accessor. Returns 0 if this accessor's {@link Accessor::type} does not have a valid + * enumeration value. + */ + int8_t computeNumberOfComponents() const; + + /** + * @brief Computes the number of bytes for this accessor's component type. + * + * For example if this accessor's {@link Accessor::componentType} is `CesiumGltf::Accessor::ComponentType::UNSIGNED_SHORT`, + * then the component type is 2 bytes, while if it's `CesiumGltf::Accessor::ComponentType::FLOAT` then it is 4 bytes. + * + * @return The number of bytes for this accessor's component type. Returns 0 if this accessor's {@link Accessor::componentType} does not + * have a valid enumeration value. + */ + int8_t computeByteSizeOfComponent() const; + + /** + * @brief Computes the total number of bytes for this accessor in each vertex. + * + * This is computed by multiplying {@link Accessor::computeByteSizeOfComponent} by {@link Accessor::computeNumberOfComponents}. + * + * @return The total number of bytes for this accessor in each vertex. Returns 0 if this accessor's {@link Accessor::type} or + * {@link Accessor::componentType} does not have a valid enumeration value. + */ + int64_t computeBytesPerVertex() const; + + /** + * @brief Computes this accessor's stride. + * + * The stride is the number of bytes between the same elements of successive vertices. The returned + * value will be at least as large as {@link Accessor::computeBytesPerVertex}, but maybe be larger if + * this accessor's data is interleaved with other accessors. + * + * The behavior is undefined if this accessor is not part of the given model. + * + * @param model The model that this accessor is a part of. + * @return The stride in bytes. Returns 0 if this accessor's {@link Accessor::type} or + * {@link Accessor::componentType} does not have a valid enumeration value, or if + * {@link Accessor::bufferView} does not refer to a valid {@link BufferView}. + */ + int64_t computeByteStride(const CesiumGltf::Model& model) const; + }; +} diff --git a/CesiumGltf/include/CesiumGltf/AccessorSparse.h b/CesiumGltf/include/CesiumGltf/AccessorSparse.h new file mode 100644 index 000000000..8c1019c1d --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/AccessorSparse.h @@ -0,0 +1,35 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/AccessorSparseIndices.h" +#include "CesiumGltf/AccessorSparseValues.h" +#include "CesiumGltf/ExtensibleObject.h" +#include "CesiumGltf/Library.h" +#include + +namespace CesiumGltf { + /** + * @brief Sparse storage of attributes that deviate from their initialization value. + */ + struct CESIUMGLTF_API AccessorSparse final : public ExtensibleObject { + + /** + * @brief Number of entries stored in the sparse array. + * + * The number of attributes encoded in this sparse accessor. + */ + int64_t count = int64_t(); + + /** + * @brief Index array of size `count` that points to those accessor attributes that deviate from their initialization value. Indices must strictly increase. + */ + AccessorSparseIndices indices; + + /** + * @brief Array of size `count` times number of components, storing the displaced accessor attributes pointed by `indices`. Substituted values must have the same `componentType` and number of components as the base accessor. + */ + AccessorSparseValues values; + + }; +} diff --git a/CesiumGltf/include/CesiumGltf/AccessorSparseIndices.h b/CesiumGltf/include/CesiumGltf/AccessorSparseIndices.h new file mode 100644 index 000000000..f484a2e6f --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/AccessorSparseIndices.h @@ -0,0 +1,40 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/ExtensibleObject.h" +#include "CesiumGltf/Library.h" +#include + +namespace CesiumGltf { + /** + * @brief Indices of those attributes that deviate from their initialization value. + */ + struct CESIUMGLTF_API AccessorSparseIndices final : public ExtensibleObject { + enum class ComponentType { + UNSIGNED_BYTE = 5121, + + UNSIGNED_SHORT = 5123, + + UNSIGNED_INT = 5125 + }; + + /** + * @brief The index of the bufferView with sparse indices. Referenced bufferView can't have ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER target. + */ + int32_t bufferView = -1; + + /** + * @brief The offset relative to the start of the bufferView in bytes. Must be aligned. + */ + int64_t byteOffset = 0; + + /** + * @brief The indices data type. + * + * Valid values correspond to WebGL enums: `5121` (UNSIGNED_BYTE), `5123` (UNSIGNED_SHORT), `5125` (UNSIGNED_INT). + */ + ComponentType componentType = ComponentType(); + + }; +} diff --git a/CesiumGltf/include/CesiumGltf/AccessorSparseValues.h b/CesiumGltf/include/CesiumGltf/AccessorSparseValues.h new file mode 100644 index 000000000..6c496b544 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/AccessorSparseValues.h @@ -0,0 +1,26 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/ExtensibleObject.h" +#include "CesiumGltf/Library.h" +#include + +namespace CesiumGltf { + /** + * @brief Array of size `accessor.sparse.count` times number of components storing the displaced accessor attributes pointed by `accessor.sparse.indices`. + */ + struct CESIUMGLTF_API AccessorSparseValues final : public ExtensibleObject { + + /** + * @brief The index of the bufferView with sparse values. Referenced bufferView can't have ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER target. + */ + int32_t bufferView = -1; + + /** + * @brief The offset relative to the start of the bufferView in bytes. Must be aligned. + */ + int64_t byteOffset = 0; + + }; +} diff --git a/CesiumGltf/include/CesiumGltf/AccessorSpec.h b/CesiumGltf/include/CesiumGltf/AccessorSpec.h new file mode 100644 index 000000000..7eb9c2fe2 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/AccessorSpec.h @@ -0,0 +1,118 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/AccessorSparse.h" +#include "CesiumGltf/Library.h" +#include "CesiumGltf/NamedObject.h" +#include +#include +#include + +namespace CesiumGltf { + /** + * @brief A typed view into a bufferView. A bufferView contains raw binary data. An accessor provides a typed view into a bufferView or a subset of a bufferView similar to how WebGL's `vertexAttribPointer()` defines an attribute in a buffer. + */ + struct CESIUMGLTF_API AccessorSpec : public NamedObject { + enum class ComponentType { + BYTE = 5120, + + UNSIGNED_BYTE = 5121, + + SHORT = 5122, + + UNSIGNED_SHORT = 5123, + + UNSIGNED_INT = 5125, + + FLOAT = 5126 + }; + + enum class Type { + SCALAR, + + VEC2, + + VEC3, + + VEC4, + + MAT2, + + MAT3, + + MAT4 + }; + + /** + * @brief The index of the bufferView. + * + * When not defined, accessor must be initialized with zeros; `sparse` property or extensions could override zeros with actual values. + */ + int32_t bufferView = -1; + + /** + * @brief The offset relative to the start of the bufferView in bytes. + * + * This must be a multiple of the size of the component datatype. + */ + int64_t byteOffset = 0; + + /** + * @brief The datatype of components in the attribute. + * + * All valid values correspond to WebGL enums. The corresponding typed arrays are `Int8Array`, `Uint8Array`, `Int16Array`, `Uint16Array`, `Uint32Array`, and `Float32Array`, respectively. 5125 (UNSIGNED_INT) is only allowed when the accessor contains indices, i.e., the accessor is only referenced by `primitive.indices`. + */ + ComponentType componentType = ComponentType(); + + /** + * @brief Specifies whether integer data values should be normalized. + * + * Specifies whether integer data values should be normalized (`true`) to [0, 1] (for unsigned types) or [-1, 1] (for signed types), or converted directly (`false`) when they are accessed. This property is defined only for accessors that contain vertex attributes or animation output data. + */ + bool normalized = false; + + /** + * @brief The number of attributes referenced by this accessor. + * + * The number of attributes referenced by this accessor, not to be confused with the number of bytes or number of components. + */ + int64_t count = int64_t(); + + /** + * @brief Specifies if the attribute is a scalar, vector, or matrix. + */ + Type type = Type(); + + /** + * @brief Maximum value of each component in this attribute. + * + * Array elements must be treated as having the same data type as accessor's `componentType`. Both min and max arrays have the same length. The length is determined by the value of the type property; it can be 1, 2, 3, 4, 9, or 16. + * + * `normalized` property has no effect on array values: they always correspond to the actual values stored in the buffer. When accessor is sparse, this property must contain max values of accessor data with sparse substitution applied. + */ + std::vector max; + + /** + * @brief Minimum value of each component in this attribute. + * + * Array elements must be treated as having the same data type as accessor's `componentType`. Both min and max arrays have the same length. The length is determined by the value of the type property; it can be 1, 2, 3, 4, 9, or 16. + * + * `normalized` property has no effect on array values: they always correspond to the actual values stored in the buffer. When accessor is sparse, this property must contain min values of accessor data with sparse substitution applied. + */ + std::vector min; + + /** + * @brief Sparse storage of attributes that deviate from their initialization value. + */ + std::optional sparse; + + private: + /** + * @brief This class is not mean to be instantiated directly. Use {@link Accessor} instead. + */ + AccessorSpec() = default; + friend struct Accessor; + + }; +} diff --git a/CesiumGltf/include/CesiumGltf/AccessorView.h b/CesiumGltf/include/CesiumGltf/AccessorView.h new file mode 100644 index 000000000..1efbd6c59 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/AccessorView.h @@ -0,0 +1,231 @@ +#pragma once + +#include "CesiumGltf/Model.h" +#include + +namespace CesiumGltf { + /** + * @brief Indicates the status of an {@link AccessorView}. + * + * The {@link AccessorView} constructor always completes successfully. However, it may not + * always reflect the actual content of the {@link Accessor}, but instead indicate that + * its {@link AccessorView::size} is 0. This enumeration provides the reason. + */ + enum class AccessorViewStatus { + /** + * @brief This accessor is valid and ready to use. + */ + Valid, + + /** + * @brief The accessor index does not refer to a valid accessor. + */ + InvalidAccessorIndex, + + /** + * @brief The accessor's bufferView index does not refer to a valid bufferView. + */ + InvalidBufferViewIndex, + + /** + * @brief The accessor's bufferView's buffer index does not refer to a valid buffer. + */ + InvalidBufferIndex, + + /** + * @brief The accessor is too large to fit in its bufferView. + */ + BufferViewTooSmall, + + /** + * @brief The accessor's bufferView is too large to fit in its buffer. + */ + BufferTooSmall, + + /** + * @brief The `sizeof(T)` does not match the accessor's {@link Accessor::computeBytesPerVertex}. + */ + WrongSizeT, + }; + + /** + * @brief A view on the data of one accessor of a glTF asset. + * + * It provides the actual accessor data like an array of elements. + * The type of the accessor elements is determined by the template + * parameter. Instances are usually from an {@link Accessor}, + * and the {@link operator[]()} can be used to access the elements: + * + * @snippet TestAccessorView.cpp createFromAccessorAndRead + * + * @tparam T The type of the elements in the accessor. + */ + template + class AccessorView final { + private: + const uint8_t* _pData; + int64_t _stride; + int64_t _offset; + int64_t _size; + AccessorViewStatus _status; + + public: + typedef T value_type; + + /** + * @brief Construct a new instance not pointing to any data. + * + * The new instance will have a {@link size} of 0 and a {@link status} of `AccessorViewStatus::InvalidAccessorIndex`. + */ + AccessorView() : + _pData(nullptr), + _stride(0), + _offset(0), + _size(0), + _status(AccessorViewStatus::InvalidAccessorIndex) + { + } + + /** + * @brief Creates a new instance from low-level parameters. + * + * The provided parameters are not validated in any way, and so this overload can easily + * be used to access invalid memory. + * + * @param pData The raw data buffer from which to read. + * @param stride The stride, in bytes, between successive elements. + * @param offset The offset from the start of the buffer to the first element. + * @param size The total number of elements. + */ + AccessorView(const uint8_t* pData, int64_t stride, int64_t offset, int64_t size) : + _pData(pData), + _stride(stride), + _offset(offset), + _size(size), + _status(AccessorViewStatus::Valid) + { + } + + /** + * @brief Creates a new instance from a given model and {@link Accessor}. + * + * If the accessor cannot be viewed, the construct will still complete successfully + * without throwing an exception. However, {@link size} will return 0 and + * {@link status} will indicate what went wrong. + * + * @param model The model to access. + * @param accessor The accessor to view. + */ + AccessorView(const Model& model, const Accessor& accessor) noexcept : + AccessorView() + { + this->create(model, accessor); + } + + + /** + * @brief Creates a new instance from a given model and accessor index. + * + * If the accessor cannot be viewed, the construct will still complete successfully + * without throwing an exception. However, {@link size} will return 0 and + * {@link status} will indicate what went wrong. + * + * @param model The model to access. + * @param accessorIndex The index of the accessor to view in the model's {@link Model::accessors} list. + */ + AccessorView(const Model& model, int32_t accessorIndex) noexcept : + AccessorView() + { + const Accessor* pAccessor = Model::getSafe(&model.accessors, accessorIndex); + if (!pAccessor) { + this->_status = AccessorViewStatus::InvalidAccessorIndex; + return; + } + + this->create(model, *pAccessor); + } + + /** + * @brief Provides the specified accessor element. + * + * @param i The index of the element. + * @returns The constant reference to the accessor element. + * @throws A `std::range_error` if the given index is negative + * or not smaller than the {@link size} of this accessor. + */ + const T& operator[](int64_t i) const { + if (i < 0 || i >= this->_size) { + throw std::range_error("index out of range"); + } + + return *reinterpret_cast(this->_pData + i * this->_stride + this->_offset); + } + + /** + * @brief Returns the size (number of elements) of this accessor. + * + * This is the number of elements of type `T` that this accessor contains. + * + * @returns The size. + */ + int64_t size() const noexcept { + return this->_size; + } + + /** + * @brief Gets the status of this accessor view. + * + * Indicates whether the view accurately reflects the accessor's data, or whether + * an error occurred. + */ + AccessorViewStatus status() const noexcept { + return this->_status; + } + + private: + void create(const Model& model, const Accessor& accessor) { + const CesiumGltf::BufferView* pBufferView = Model::getSafe(&model.bufferViews, accessor.bufferView); + if (!pBufferView) { + this->_status = AccessorViewStatus::InvalidBufferViewIndex; + return; + } + + const CesiumGltf::Buffer* pBuffer = Model::getSafe(&model.buffers, pBufferView->buffer); + if (!pBuffer) { + this->_status = AccessorViewStatus::InvalidBufferIndex; + return; + } + + const std::vector& data = pBuffer->cesium.data; + int64_t bufferBytes = data.size(); + if (pBufferView->byteOffset + pBufferView->byteLength > bufferBytes) { + this->_status = AccessorViewStatus::BufferTooSmall; + return; + } + + int64_t accessorByteStride = accessor.computeByteStride(model); + int64_t accessorComponentElements = accessor.computeNumberOfComponents(); + int64_t accessorComponentBytes = accessor.computeByteSizeOfComponent(); + int64_t accessorBytesPerStride = accessorComponentElements * accessorComponentBytes; + + if (sizeof(T) != accessorBytesPerStride) { + this->_status = AccessorViewStatus::WrongSizeT; + return; + } + + int64_t accessorBytes = accessorByteStride * accessor.count; + int64_t bytesRemainingInBufferView = pBufferView->byteLength - (accessor.byteOffset + accessorByteStride * (accessor.count - 1) + accessorBytesPerStride); + if (accessorBytes > pBufferView->byteLength || bytesRemainingInBufferView < 0) { + this->_status = AccessorViewStatus::BufferViewTooSmall; + return; + } + + this->_pData = pBuffer->cesium.data.data(); + this->_stride = accessorByteStride; + this->_offset = accessor.byteOffset + pBufferView->byteOffset; + this->_size = accessor.count; + this->_status = AccessorViewStatus::Valid; + } + }; + +} diff --git a/CesiumGltf/include/CesiumGltf/AccessorWriter.h b/CesiumGltf/include/CesiumGltf/AccessorWriter.h new file mode 100644 index 000000000..ea70089fa --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/AccessorWriter.h @@ -0,0 +1,63 @@ +#pragma once + +#include "CesiumGltf/AccessorView.h" + +namespace CesiumGltf { + + /** + * @brief Provides write access to an {@link AccessorView}. + */ + template + class AccessorWriter final { + private: + AccessorView _accessor; + + public: + AccessorWriter() : + _accessor() + { + } + + /** @copydoc AccessorView::AccessorView */ + AccessorWriter(uint8_t* pData, int64_t stride, int64_t offset, int64_t size) : + _accessor(pData, stride, offset, size) + { + } + + AccessorWriter(Model& model, const Accessor& accessor) : + _accessor(model, accessor) + { + } + + AccessorWriter(Model& model, int32_t accessorIndex) : + _accessor(model, accessorIndex) + { + } + + /** @copydoc AccessorView::operator[]() */ + const T& operator[](int64_t i) const { + return this->_accessor[i]; + } + + /** @copydoc AccessorView::operator[]() */ + T& operator[](int64_t i) { + return const_cast(this->_accessor[i]); + } + + /** @copydoc AccessorView::size */ + int64_t size() const noexcept { + return this->_accessor.size(); + } + + /** + * @brief Gets the status of this accessor writer. + * + * Indicates whether the writer accurately reflects the accessor's data, or whether + * an error occurred. + */ + AccessorViewStatus status() const noexcept { + return this->_accessor.status(); + } + }; + +} \ No newline at end of file diff --git a/CesiumGltf/include/CesiumGltf/Animation.h b/CesiumGltf/include/CesiumGltf/Animation.h new file mode 100644 index 000000000..be2d6e4fd --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/Animation.h @@ -0,0 +1,28 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/AnimationChannel.h" +#include "CesiumGltf/AnimationSampler.h" +#include "CesiumGltf/Library.h" +#include "CesiumGltf/NamedObject.h" +#include + +namespace CesiumGltf { + /** + * @brief A keyframe animation. + */ + struct CESIUMGLTF_API Animation final : public NamedObject { + + /** + * @brief An array of channels, each of which targets an animation's sampler at a node's property. Different channels of the same animation can't have equal targets. + */ + std::vector channels; + + /** + * @brief An array of samplers that combines input and output accessors with an interpolation algorithm to define a keyframe graph (but not its target). + */ + std::vector samplers; + + }; +} diff --git a/CesiumGltf/include/CesiumGltf/AnimationChannel.h b/CesiumGltf/include/CesiumGltf/AnimationChannel.h new file mode 100644 index 000000000..96fc660d4 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/AnimationChannel.h @@ -0,0 +1,29 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/AnimationChannelTarget.h" +#include "CesiumGltf/ExtensibleObject.h" +#include "CesiumGltf/Library.h" +#include + +namespace CesiumGltf { + /** + * @brief Targets an animation's sampler at a node's property. + */ + struct CESIUMGLTF_API AnimationChannel final : public ExtensibleObject { + + /** + * @brief The index of a sampler in this animation used to compute the value for the target. + * + * The index of a sampler in this animation used to compute the value for the target, e.g., a node's translation, rotation, or scale (TRS). + */ + int32_t sampler = -1; + + /** + * @brief The index of the node and TRS property to target. + */ + AnimationChannelTarget target; + + }; +} diff --git a/CesiumGltf/include/CesiumGltf/AnimationChannelTarget.h b/CesiumGltf/include/CesiumGltf/AnimationChannelTarget.h new file mode 100644 index 000000000..6c95148eb --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/AnimationChannelTarget.h @@ -0,0 +1,35 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/ExtensibleObject.h" +#include "CesiumGltf/Library.h" +#include + +namespace CesiumGltf { + /** + * @brief The index of the node and TRS property that an animation channel targets. + */ + struct CESIUMGLTF_API AnimationChannelTarget final : public ExtensibleObject { + enum class Path { + translation, + + rotation, + + scale, + + weights + }; + + /** + * @brief The index of the node to target. + */ + int32_t node = -1; + + /** + * @brief The name of the node's TRS property to modify, or the "weights" of the Morph Targets it instantiates. For the "translation" property, the values that are provided by the sampler are the translation along the x, y, and z axes. For the "rotation" property, the values are a quaternion in the order (x, y, z, w), where w is the scalar. For the "scale" property, the values are the scaling factors along the x, y, and z axes. + */ + Path path = Path(); + + }; +} diff --git a/CesiumGltf/include/CesiumGltf/AnimationSampler.h b/CesiumGltf/include/CesiumGltf/AnimationSampler.h new file mode 100644 index 000000000..f8d2a8fa1 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/AnimationSampler.h @@ -0,0 +1,42 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/ExtensibleObject.h" +#include "CesiumGltf/Library.h" +#include + +namespace CesiumGltf { + /** + * @brief Combines input and output accessors with an interpolation algorithm to define a keyframe graph (but not its target). + */ + struct CESIUMGLTF_API AnimationSampler final : public ExtensibleObject { + enum class Interpolation { + LINEAR, + + STEP, + + CUBICSPLINE + }; + + /** + * @brief The index of an accessor containing keyframe input values, e.g., time. + * + * That accessor must have componentType `FLOAT`. The values represent time in seconds with `time[0] >= 0.0`, and strictly increasing values, i.e., `time[n + 1] > time[n]`. + */ + int32_t input = -1; + + /** + * @brief Interpolation algorithm. + */ + Interpolation interpolation = Interpolation::LINEAR; + + /** + * @brief The index of an accessor, containing keyframe output values. + * + * The index of an accessor containing keyframe output values. When targeting translation or scale paths, the `accessor.componentType` of the output values must be `FLOAT`. When targeting rotation or morph weights, the `accessor.componentType` of the output values must be `FLOAT` or normalized integer. For weights, each output element stores `SCALAR` values with a count equal to the number of morph targets. + */ + int32_t output = -1; + + }; +} diff --git a/CesiumGltf/include/CesiumGltf/Asset.h b/CesiumGltf/include/CesiumGltf/Asset.h new file mode 100644 index 000000000..69cf43fea --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/Asset.h @@ -0,0 +1,37 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/ExtensibleObject.h" +#include "CesiumGltf/Library.h" +#include +#include + +namespace CesiumGltf { + /** + * @brief Metadata about the glTF asset. + */ + struct CESIUMGLTF_API Asset final : public ExtensibleObject { + + /** + * @brief A copyright message suitable for display to credit the content creator. + */ + std::optional copyright; + + /** + * @brief Tool that generated this glTF model. Useful for debugging. + */ + std::optional generator; + + /** + * @brief The glTF version that this asset targets. + */ + std::string version; + + /** + * @brief The minimum glTF version that this asset targets. + */ + std::optional minVersion; + + }; +} diff --git a/CesiumGltf/include/CesiumGltf/Buffer.h b/CesiumGltf/include/CesiumGltf/Buffer.h new file mode 100644 index 000000000..47a6bf2b9 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/Buffer.h @@ -0,0 +1,15 @@ +#pragma once + +#include "CesiumGltf/BufferCesium.h" +#include "CesiumGltf/BufferSpec.h" +#include "CesiumGltf/Library.h" + +namespace CesiumGltf { + /** @copydoc BufferSpec */ + struct CESIUMGLTF_API Buffer final : public BufferSpec { + /** + * @brief Holds properties that are specific to the glTF loader rather than part of the glTF spec. + */ + BufferCesium cesium; + }; +} diff --git a/CesiumGltf/include/CesiumGltf/BufferCesium.h b/CesiumGltf/include/CesiumGltf/BufferCesium.h new file mode 100644 index 000000000..4e4ed068a --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/BufferCesium.h @@ -0,0 +1,16 @@ +#pragma once + +#include "CesiumGltf/Library.h" +#include + +namespace CesiumGltf { + /** + * @brief Holds {@link Buffer} properties that are specific to the glTF loader rather than part of the glTF spec. + */ + struct CESIUMGLTF_API BufferCesium final { + /** + * @brief The buffer's data. + */ + std::vector data; + }; +} diff --git a/CesiumGltf/include/CesiumGltf/BufferSpec.h b/CesiumGltf/include/CesiumGltf/BufferSpec.h new file mode 100644 index 000000000..b3c355e2f --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/BufferSpec.h @@ -0,0 +1,37 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/Library.h" +#include "CesiumGltf/NamedObject.h" +#include +#include +#include + +namespace CesiumGltf { + /** + * @brief A buffer points to binary geometry, animation, or skins. + */ + struct CESIUMGLTF_API BufferSpec : public NamedObject { + + /** + * @brief The uri of the buffer. + * + * Relative paths are relative to the .gltf file. Instead of referencing an external file, the uri can also be a data-uri. + */ + std::optional uri; + + /** + * @brief The length of the buffer in bytes. + */ + int64_t byteLength = int64_t(); + + private: + /** + * @brief This class is not mean to be instantiated directly. Use {@link Buffer} instead. + */ + BufferSpec() = default; + friend struct Buffer; + + }; +} diff --git a/CesiumGltf/include/CesiumGltf/BufferView.h b/CesiumGltf/include/CesiumGltf/BufferView.h new file mode 100644 index 000000000..9f6816ab4 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/BufferView.h @@ -0,0 +1,49 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/Library.h" +#include "CesiumGltf/NamedObject.h" +#include +#include + +namespace CesiumGltf { + /** + * @brief A view into a buffer generally representing a subset of the buffer. + */ + struct CESIUMGLTF_API BufferView final : public NamedObject { + enum class Target { + ARRAY_BUFFER = 34962, + + ELEMENT_ARRAY_BUFFER = 34963 + }; + + /** + * @brief The index of the buffer. + */ + int32_t buffer = -1; + + /** + * @brief The offset into the buffer in bytes. + */ + int64_t byteOffset = 0; + + /** + * @brief The length of the bufferView in bytes. + */ + int64_t byteLength = int64_t(); + + /** + * @brief The stride, in bytes. + * + * The stride, in bytes, between vertex attributes. When this is not defined, data is tightly packed. When two or more accessors use the same bufferView, this field must be defined. + */ + std::optional byteStride; + + /** + * @brief The target that the GPU buffer should be bound to. + */ + std::optional target; + + }; +} diff --git a/CesiumGltf/include/CesiumGltf/Camera.h b/CesiumGltf/include/CesiumGltf/Camera.h new file mode 100644 index 000000000..e3aa50881 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/Camera.h @@ -0,0 +1,40 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/CameraOrthographic.h" +#include "CesiumGltf/CameraPerspective.h" +#include "CesiumGltf/Library.h" +#include "CesiumGltf/NamedObject.h" +#include + +namespace CesiumGltf { + /** + * @brief A camera's projection. A node can reference a camera to apply a transform to place the camera in the scene. + */ + struct CESIUMGLTF_API Camera final : public NamedObject { + enum class Type { + perspective, + + orthographic + }; + + /** + * @brief An orthographic camera containing properties to create an orthographic projection matrix. + */ + std::optional orthographic; + + /** + * @brief A perspective camera containing properties to create a perspective projection matrix. + */ + std::optional perspective; + + /** + * @brief Specifies if the camera uses a perspective or orthographic projection. + * + * Based on this, either the camera's `perspective` or `orthographic` property will be defined. + */ + Type type = Type(); + + }; +} diff --git a/CesiumGltf/include/CesiumGltf/CameraOrthographic.h b/CesiumGltf/include/CesiumGltf/CameraOrthographic.h new file mode 100644 index 000000000..c354bf7f9 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/CameraOrthographic.h @@ -0,0 +1,35 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/ExtensibleObject.h" +#include "CesiumGltf/Library.h" + +namespace CesiumGltf { + /** + * @brief An orthographic camera containing properties to create an orthographic projection matrix. + */ + struct CESIUMGLTF_API CameraOrthographic final : public ExtensibleObject { + + /** + * @brief The floating-point horizontal magnification of the view. Must not be zero. + */ + double xmag = double(); + + /** + * @brief The floating-point vertical magnification of the view. Must not be zero. + */ + double ymag = double(); + + /** + * @brief The floating-point distance to the far clipping plane. `zfar` must be greater than `znear`. + */ + double zfar = double(); + + /** + * @brief The floating-point distance to the near clipping plane. + */ + double znear = double(); + + }; +} diff --git a/CesiumGltf/include/CesiumGltf/CameraPerspective.h b/CesiumGltf/include/CesiumGltf/CameraPerspective.h new file mode 100644 index 000000000..af427d1c2 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/CameraPerspective.h @@ -0,0 +1,40 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/ExtensibleObject.h" +#include "CesiumGltf/Library.h" +#include + +namespace CesiumGltf { + /** + * @brief A perspective camera containing properties to create a perspective projection matrix. + */ + struct CESIUMGLTF_API CameraPerspective final : public ExtensibleObject { + + /** + * @brief The floating-point aspect ratio of the field of view. + * + * When this is undefined, the aspect ratio of the canvas is used. + */ + std::optional aspectRatio; + + /** + * @brief The floating-point vertical field of view in radians. + */ + double yfov = double(); + + /** + * @brief The floating-point distance to the far clipping plane. + * + * When defined, `zfar` must be greater than `znear`. If `zfar` is undefined, runtime must use infinite projection matrix. + */ + std::optional zfar; + + /** + * @brief The floating-point distance to the near clipping plane. + */ + double znear = double(); + + }; +} diff --git a/CesiumGltf/include/CesiumGltf/ExtensibleObject.h b/CesiumGltf/include/CesiumGltf/ExtensibleObject.h new file mode 100644 index 000000000..c855c6921 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/ExtensibleObject.h @@ -0,0 +1,52 @@ +#pragma once + +#include "CesiumGltf/JsonValue.h" +#include "CesiumGltf/Library.h" +#include +#include + +namespace CesiumGltf { + /** + * @brief The base class for objects in a glTF that have extensions and extras. + */ + struct CESIUMGLTF_API ExtensibleObject { + // TODO: extras + + /** + * @brief Gets an extension given its static type. + * + * @tparam T The type of the extension. + * @return A pointer to the extension, or nullptr if the extension is not attached to this object. + */ + template + const T* getExtension() const noexcept { + for (const std::any& extension : extensions) { + const T* p = std::any_cast(&extension); + if (p) { + return p; + } + } + return nullptr; + } + + /** @copydoc ExtensionObject::getExtension */ + template + T* getExtension() noexcept { + return const_cast(const_cast(this)->getExtension()); + } + + /** + * @brief The extensions attached to this object. + * + * Use {@link getExtension} to get the extension with a particular static type. + */ + std::vector extensions; + + /** + * @brief Application-specific data. + * + * **Implementation Note:** Although extras may have any type, it is common for applications to store and access custom data as key/value pairs. As best practice, extras should be an Object rather than a primitive value for best portability. + */ + JsonValue::Object extras; + }; +} diff --git a/CesiumGltf/include/CesiumGltf/Image.h b/CesiumGltf/include/CesiumGltf/Image.h new file mode 100644 index 000000000..d34d4aa3d --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/Image.h @@ -0,0 +1,15 @@ +#pragma once + +#include "CesiumGltf/ImageCesium.h" +#include "CesiumGltf/ImageSpec.h" +#include "CesiumGltf/Library.h" + +namespace CesiumGltf { + /** @copydoc ImageSpec */ + struct CESIUMGLTF_API Image final : public ImageSpec { + /** + * @brief Holds properties that are specific to the glTF loader rather than part of the glTF spec. + */ + ImageCesium cesium; + }; +} diff --git a/CesiumGltf/include/CesiumGltf/ImageCesium.h b/CesiumGltf/include/CesiumGltf/ImageCesium.h new file mode 100644 index 000000000..0e446b039 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/ImageCesium.h @@ -0,0 +1,52 @@ +#pragma once + +#include "CesiumGltf/Library.h" +#include +#include + +namespace CesiumGltf { + /** + * @brief Holds {@link Image} properties that are specific to the glTF loader rather than part of the glTF spec. + */ + struct CESIUMGLTF_API ImageCesium final { + /** + * @brief The width of the image in pixels. + */ + int32_t width; + + /** + * @brief The height of the image in pixels. + */ + int32_t height; + + /** + * @brief The number of channels per pixel. + */ + int32_t channels; + + /** + * @brief The number of bytes per channel. + */ + int32_t bytesPerChannel; + + /** + * @brief The raw pixel data. + * + * The pixel data is consistent with the [stb](https://github.com/nothings/stb) image library. + * + * For a correctly-formed image, the size of the array will be + * `width * height * channels * bytesPerChannel` bytes. There is no + * padding between rows or columns of the image, regardless of format. + * + * The channels and their meaning are as follows: + * + * | Number of Channels | Channel Order and Meaning | + * |--------------------|---------------------------| + * | 1 | grey | + * | 2 | grey, alpha | + * | 3 | red, green, blue | + * | 4 | red, green, blue, alpha | + */ + std::vector pixelData; + }; +} diff --git a/CesiumGltf/include/CesiumGltf/ImageSpec.h b/CesiumGltf/include/CesiumGltf/ImageSpec.h new file mode 100644 index 000000000..02769ee68 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/ImageSpec.h @@ -0,0 +1,47 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/Library.h" +#include "CesiumGltf/NamedObject.h" +#include +#include +#include + +namespace CesiumGltf { + /** + * @brief Image data used to create a texture. Image can be referenced by URI or `bufferView` index. `mimeType` is required in the latter case. + */ + struct CESIUMGLTF_API ImageSpec : public NamedObject { + enum class MimeType { + image_jpeg, + + image_png + }; + + /** + * @brief The uri of the image. + * + * Relative paths are relative to the .gltf file. Instead of referencing an external file, the uri can also be a data-uri. The image format must be jpg or png. + */ + std::optional uri; + + /** + * @brief The image's MIME type. Required if `bufferView` is defined. + */ + std::optional mimeType; + + /** + * @brief The index of the bufferView that contains the image. Use this instead of the image's uri property. + */ + int32_t bufferView = -1; + + private: + /** + * @brief This class is not mean to be instantiated directly. Use {@link Image} instead. + */ + ImageSpec() = default; + friend struct Image; + + }; +} diff --git a/CesiumGltf/include/CesiumGltf/JsonValue.h b/CesiumGltf/include/CesiumGltf/JsonValue.h new file mode 100644 index 000000000..3fa2c3eb3 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/JsonValue.h @@ -0,0 +1,148 @@ +#pragma once + +#include "CesiumGltf/Library.h" +#include +#include +#include +#include +#include +#include + +namespace CesiumGltf { + class CESIUMGLTF_API JsonValue final { + public: + using Null = std::nullptr_t; + using Number = double; + using Bool = bool; + using String = std::string; + using Object = std::map; + using Array = std::vector; + + JsonValue() : value() {} + JsonValue(std::nullptr_t) : value(nullptr) {} + JsonValue(double v) : value(v) {} + JsonValue(int8_t v) : JsonValue(static_cast(v)) {} + JsonValue(uint8_t v) : JsonValue(static_cast(v)) {} + JsonValue(int16_t v) : JsonValue(static_cast(v)) {} + JsonValue(uint16_t v) : JsonValue(static_cast(v)) {} + JsonValue(int32_t v) : JsonValue(static_cast(v)) {} + JsonValue(uint32_t v) : JsonValue(static_cast(v)) {} + JsonValue(int64_t v) : JsonValue(static_cast(v)) {} + JsonValue(uint64_t v) : JsonValue(static_cast(v)) {} + JsonValue(bool v) : value(v) {} + JsonValue(const std::string& v) : value(v) {} + JsonValue(std::string&& v) : value(std::move(v)) {} + JsonValue(const char* v) : value(std::string(v)) {} + JsonValue(const std::map& v) : value(v) {} + JsonValue(std::map&& v) : value(std::move(v)) {} + JsonValue(const std::vector& v) : value(v) {} + JsonValue(std::vector&& v) : value(std::move(v)) {} + + JsonValue(std::initializer_list v) : + value(std::vector(v)) {} + JsonValue(std::initializer_list> v) : + value(std::map(v)) {} + + /** + * @brief Gets the number from the value, or a default if the value does not contain a number. + * + * @param defaultValue The default value to return if the value is not a number. + * @return The number. + */ + double getNumber(double defaultValue) const; + + /** + * @brief Gets the bool from the value, or a default if the value does not contain a bool. + * + * @param defaultValue The default value to return if the value is not a bool. + * @return The bool. + */ + bool getBool(bool defaultValue) const; + + /** + * @brief Gets the string from the value, or a default if the value does not contain a string. + * + * @param defaultValue The default value to return if the value is not a string. + * @return The string. + */ + std::string getString(const std::string& defaultValue) const; + + /** + * @brief Gets a typed value corresponding to the given key in the object represented by this instance. + * + * If this instance is not a {@link JsonValue::Object}, returns `nullptr`. If the key does not exist in + * this object, returns `nullptr`. If the named value does not have the type T, returns nullptr. + * + * @tparam T The expected type of the value. + * @param key The key for which to retrieve the value from this object. + * @return A pointer to the requested value, or nullptr if the value cannot be obtained as requested. + */ + template + const T* getValueForKey(const std::string& key) const { + const JsonValue* pValue = this->getValueForKey(key); + if (!pValue) { + return nullptr; + } + + return std::get_if(&pValue->value); + } + + /** + * @brief Gets a typed value corresponding to the given key in the object represented by this instance. + * + * If this instance is not a {@link JsonValue::Object}, returns `nullptr`. If the key does not exist in + * this object, returns `nullptr`. If the named value does not have the type T, returns nullptr. + * + * @tparam T The expected type of the value. + * @param key The key for which to retrieve the value from this object. + * @return A pointer to the requested value, or nullptr if the value cannot be obtained as requested. + */ + template + T* getValueForKey(const std::string& key) { + JsonValue* pValue = this->getValueForKey(key); + if (!pValue) { + return nullptr; + } + + return std::get_if(&pValue->value); + } + + /** + * @brief Gets the value corresponding to the given key in the object represented by this instance. + * + * If this instance is not a {@link JsonValue::Object}, returns `nullptr`. If the key does not exist in + * this object, returns `nullptr`. If the named value does not have the type T, returns nullptr. + * + * @param key The key for which to retrieve the value from this object. + * @return A pointer to the requested value, or nullptr if the value cannot be obtained as requested. + */ + const JsonValue* getValueForKey(const std::string& key) const; + + /** + * @brief Gets the value corresponding to the given key in the object represented by this instance. + * + * If this instance is not a {@link JsonValue::Object}, returns `nullptr`. If the key does not exist in + * this object, returns `nullptr`. If the named value does not have the type T, returns nullptr. + * + * @param key The key for which to retrieve the value from this object. + * @return A pointer to the requested value, or nullptr if the value cannot be obtained as requested. + */ + JsonValue* getValueForKey(const std::string& key); + + bool isNull() const; + bool isNumber() const; + bool isBool() const; + bool isString() const; + bool isObject() const; + bool isArray() const; + + std::variant< + Null, + Number, + Bool, + String, + Object, + Array + > value; + }; +} diff --git a/CesiumGltf/include/CesiumGltf/KHR_draco_mesh_compression.h b/CesiumGltf/include/CesiumGltf/KHR_draco_mesh_compression.h new file mode 100644 index 000000000..6ed3a992d --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/KHR_draco_mesh_compression.h @@ -0,0 +1,27 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/ExtensibleObject.h" +#include "CesiumGltf/Library.h" +#include +#include + +namespace CesiumGltf { + /** + * @brief undefined + */ + struct CESIUMGLTF_API KHR_draco_mesh_compression final : public ExtensibleObject { + + /** + * @brief The index of the bufferView. + */ + int32_t bufferView = -1; + + /** + * @brief A dictionary object, where each key corresponds to an attribute and its unique attribute id stored in the compressed geometry. + */ + std::unordered_map attributes; + + }; +} diff --git a/CesiumGltf/include/CesiumGltf/Library.h b/CesiumGltf/include/CesiumGltf/Library.h new file mode 100644 index 000000000..4c2bcd144 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/Library.h @@ -0,0 +1,16 @@ +#pragma once + +/** + * @brief Classes for working with [glTF](https://www.khronos.org/gltf/) models. + */ +namespace CesiumGltf {} + +#if defined(_WIN32) && defined(CESIUM_SHARED) + #ifdef CESIUMGLTF_BUILDING + #define CESIUMGLTF_API __declspec(dllexport) + #else + #define CESIUMGLTF_API __declspec(dllimport) + #endif +#else + #define CESIUMGLTF_API +#endif diff --git a/CesiumGltf/include/CesiumGltf/Material.h b/CesiumGltf/include/CesiumGltf/Material.h new file mode 100644 index 000000000..5596b7388 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/Material.h @@ -0,0 +1,82 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/Library.h" +#include "CesiumGltf/MaterialNormalTextureInfo.h" +#include "CesiumGltf/MaterialOcclusionTextureInfo.h" +#include "CesiumGltf/MaterialPBRMetallicRoughness.h" +#include "CesiumGltf/NamedObject.h" +#include "CesiumGltf/TextureInfo.h" +#include +#include + +namespace CesiumGltf { + /** + * @brief The material appearance of a primitive. + */ + struct CESIUMGLTF_API Material final : public NamedObject { + enum class AlphaMode { + OPAQUE, + + MASK, + + BLEND + }; + + /** + * @brief A set of parameter values that are used to define the metallic-roughness material model from Physically-Based Rendering (PBR) methodology. When not specified, all the default values of `pbrMetallicRoughness` apply. + */ + std::optional pbrMetallicRoughness; + + /** + * @brief The normal map texture. + * + * A tangent space normal map. The texture contains RGB components in linear space. Each texel represents the XYZ components of a normal vector in tangent space. Red [0 to 255] maps to X [-1 to 1]. Green [0 to 255] maps to Y [-1 to 1]. Blue [128 to 255] maps to Z [1/255 to 1]. The normal vectors use the convention +X is right and +Y is up. +Z points toward the viewer. In GLSL, this vector would be unpacked like so: `vec3 normalVector = tex2D(, texCoord) * 2 - 1`. Client implementations should normalize the normal vectors before using them in lighting equations. + */ + std::optional normalTexture; + + /** + * @brief The occlusion map texture. + * + * The occlusion values are sampled from the R channel. Higher values indicate areas that should receive full indirect lighting and lower values indicate no indirect lighting. These values are linear. If other channels are present (GBA), they are ignored for occlusion calculations. + */ + std::optional occlusionTexture; + + /** + * @brief The emissive map texture. + * + * The emissive map controls the color and intensity of the light being emitted by the material. This texture contains RGB components encoded with the sRGB transfer function. If a fourth component (A) is present, it is ignored. + */ + std::optional emissiveTexture; + + /** + * @brief The emissive color of the material. + * + * The RGB components of the emissive color of the material. These values are linear. If an emissiveTexture is specified, this value is multiplied with the texel values. + */ + std::vector emissiveFactor = { 0,0,0 }; + + /** + * @brief The alpha rendering mode of the material. + * + * The material's alpha rendering mode enumeration specifying the interpretation of the alpha value of the main factor and texture. + */ + AlphaMode alphaMode = AlphaMode::OPAQUE; + + /** + * @brief The alpha cutoff value of the material. + * + * Specifies the cutoff threshold when in `MASK` mode. If the alpha value is greater than or equal to this value then it is rendered as fully opaque, otherwise, it is rendered as fully transparent. A value greater than 1.0 will render the entire material as fully transparent. This value is ignored for other modes. + */ + double alphaCutoff = 0.5; + + /** + * @brief Specifies whether the material is double sided. + * + * When this value is false, back-face culling is enabled. When this value is true, back-face culling is disabled and double sided lighting is enabled. The back-face must have its normals reversed before the lighting equation is evaluated. + */ + bool doubleSided = false; + + }; +} diff --git a/CesiumGltf/include/CesiumGltf/MaterialNormalTextureInfo.h b/CesiumGltf/include/CesiumGltf/MaterialNormalTextureInfo.h new file mode 100644 index 000000000..8f2fa5d4d --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/MaterialNormalTextureInfo.h @@ -0,0 +1,22 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/Library.h" +#include "CesiumGltf/TextureInfo.h" + +namespace CesiumGltf { + /** + * @brief undefined + */ + struct CESIUMGLTF_API MaterialNormalTextureInfo final : public TextureInfo { + + /** + * @brief The scalar multiplier applied to each normal vector of the normal texture. + * + * The scalar multiplier applied to each normal vector of the texture. This value scales the normal vector using the formula: `scaledNormal = normalize(( * 2.0 - 1.0) * vec3(, , 1.0))`. This value is ignored if normalTexture is not specified. This value is linear. + */ + double scale = 1; + + }; +} diff --git a/CesiumGltf/include/CesiumGltf/MaterialOcclusionTextureInfo.h b/CesiumGltf/include/CesiumGltf/MaterialOcclusionTextureInfo.h new file mode 100644 index 000000000..917c370cd --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/MaterialOcclusionTextureInfo.h @@ -0,0 +1,22 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/Library.h" +#include "CesiumGltf/TextureInfo.h" + +namespace CesiumGltf { + /** + * @brief undefined + */ + struct CESIUMGLTF_API MaterialOcclusionTextureInfo final : public TextureInfo { + + /** + * @brief A scalar multiplier controlling the amount of occlusion applied. + * + * A value of 0.0 means no occlusion. A value of 1.0 means full occlusion. This value affects the resulting color using the formula: `occludedColor = lerp(color, color * , )`. This value is ignored if the corresponding texture is not specified. This value is linear. + */ + double strength = 1; + + }; +} diff --git a/CesiumGltf/include/CesiumGltf/MaterialPBRMetallicRoughness.h b/CesiumGltf/include/CesiumGltf/MaterialPBRMetallicRoughness.h new file mode 100644 index 000000000..3cc2e13df --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/MaterialPBRMetallicRoughness.h @@ -0,0 +1,53 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/ExtensibleObject.h" +#include "CesiumGltf/Library.h" +#include "CesiumGltf/TextureInfo.h" +#include +#include + +namespace CesiumGltf { + /** + * @brief A set of parameter values that are used to define the metallic-roughness material model from Physically-Based Rendering (PBR) methodology. + */ + struct CESIUMGLTF_API MaterialPBRMetallicRoughness final : public ExtensibleObject { + + /** + * @brief The material's base color factor. + * + * The RGBA components of the base color of the material. The fourth component (A) is the alpha coverage of the material. The `alphaMode` property specifies how alpha is interpreted. These values are linear. If a baseColorTexture is specified, this value is multiplied with the texel values. + */ + std::vector baseColorFactor = { 1,1,1,1 }; + + /** + * @brief The base color texture. + * + * The first three components (RGB) are encoded with the sRGB transfer function. They specify the base color of the material. If the fourth component (A) is present, it represents the linear alpha coverage of the material. Otherwise, an alpha of 1.0 is assumed. The `alphaMode` property specifies how alpha is interpreted. The stored texels must not be premultiplied. + */ + std::optional baseColorTexture; + + /** + * @brief The metalness of the material. + * + * A value of 1.0 means the material is a metal. A value of 0.0 means the material is a dielectric. Values in between are for blending between metals and dielectrics such as dirty metallic surfaces. This value is linear. If a metallicRoughnessTexture is specified, this value is multiplied with the metallic texel values. + */ + double metallicFactor = 1; + + /** + * @brief The roughness of the material. + * + * A value of 1.0 means the material is completely rough. A value of 0.0 means the material is completely smooth. This value is linear. If a metallicRoughnessTexture is specified, this value is multiplied with the roughness texel values. + */ + double roughnessFactor = 1; + + /** + * @brief The metallic-roughness texture. + * + * The metalness values are sampled from the B channel. The roughness values are sampled from the G channel. These values are linear. If other channels are present (R or A), they are ignored for metallic-roughness calculations. + */ + std::optional metallicRoughnessTexture; + + }; +} diff --git a/CesiumGltf/include/CesiumGltf/Mesh.h b/CesiumGltf/include/CesiumGltf/Mesh.h new file mode 100644 index 000000000..e679b6706 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/Mesh.h @@ -0,0 +1,27 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/Library.h" +#include "CesiumGltf/MeshPrimitive.h" +#include "CesiumGltf/NamedObject.h" +#include + +namespace CesiumGltf { + /** + * @brief A set of primitives to be rendered. A node can contain one mesh. A node's transform places the mesh in the scene. + */ + struct CESIUMGLTF_API Mesh final : public NamedObject { + + /** + * @brief An array of primitives, each defining geometry to be rendered with a material. + */ + std::vector primitives; + + /** + * @brief Array of weights to be applied to the Morph Targets. + */ + std::vector weights; + + }; +} diff --git a/CesiumGltf/include/CesiumGltf/MeshPrimitive.h b/CesiumGltf/include/CesiumGltf/MeshPrimitive.h new file mode 100644 index 000000000..2172fab35 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/MeshPrimitive.h @@ -0,0 +1,62 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/ExtensibleObject.h" +#include "CesiumGltf/Library.h" +#include +#include +#include + +namespace CesiumGltf { + /** + * @brief Geometry to be rendered with the given material. + */ + struct CESIUMGLTF_API MeshPrimitive final : public ExtensibleObject { + enum class Mode { + POINTS = 0, + + LINES = 1, + + LINE_LOOP = 2, + + LINE_STRIP = 3, + + TRIANGLES = 4, + + TRIANGLE_STRIP = 5, + + TRIANGLE_FAN = 6 + }; + + /** + * @brief A dictionary object, where each key corresponds to mesh attribute semantic and each value is the index of the accessor containing attribute's data. + */ + std::unordered_map attributes; + + /** + * @brief The index of the accessor that contains the indices. + * + * The index of the accessor that contains mesh indices. When this is not defined, the primitives should be rendered without indices using `drawArrays()`. When defined, the accessor must contain indices: the `bufferView` referenced by the accessor should have a `target` equal to 34963 (ELEMENT_ARRAY_BUFFER); `componentType` must be 5121 (UNSIGNED_BYTE), 5123 (UNSIGNED_SHORT) or 5125 (UNSIGNED_INT), the latter may require enabling additional hardware support; `type` must be `"SCALAR"`. For triangle primitives, the front face has a counter-clockwise (CCW) winding order. Values of the index accessor must not include the maximum value for the given component type, which triggers primitive restart in several graphics APIs and would require client implementations to rebuild the index buffer. Primitive restart values are disallowed and all index values must refer to actual vertices. As a result, the index accessor's values must not exceed the following maxima: BYTE `< 255`, UNSIGNED_SHORT `< 65535`, UNSIGNED_INT `< 4294967295`. + */ + int32_t indices = -1; + + /** + * @brief The index of the material to apply to this primitive when rendering. + */ + int32_t material = -1; + + /** + * @brief The type of primitives to render. + * + * All valid values correspond to WebGL enums. + */ + Mode mode = Mode(4); + + /** + * @brief An array of Morph Targets, each Morph Target is a dictionary mapping attributes (only `POSITION`, `NORMAL`, and `TANGENT` supported) to their deviations in the Morph Target. + */ + std::vector> targets; + + }; +} diff --git a/CesiumGltf/include/CesiumGltf/Model.h b/CesiumGltf/include/CesiumGltf/Model.h new file mode 100644 index 000000000..cdf2582d5 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/Model.h @@ -0,0 +1,74 @@ +#pragma once + +#include "CesiumGltf/Library.h" +#include "CesiumGltf/ModelSpec.h" + +namespace CesiumGltf { + /** @copydoc ModelSpec */ + struct CESIUMGLTF_API Model : public ModelSpec { + /** + * @brief Merges another model into this one. + * + * After this method returns, this `Model` contains all of the + * elements that were originally in it _plus_ all of the elements + * that were in `rhs`. Element indices are updated accordingly. + * However, element indices in {@ExtensibleObject::extras}, if any, + * are _not_ updated. + * + * @param rhs The model to merge into this one. + */ + void merge(Model&& rhs); + + /** + * @brief Safely gets the element with a given index, returning a default instance if the index is outside the range. + * + * @tparam T The type of the array. + * @param items The array. + * @param index The index of the array element to retrieve. + * @return The requested element, or a default instance if the index is invalid. + */ + template + static const T& getSafe(const std::vector& items, int32_t index) { + static T defaultObject; + if (index < 0 || static_cast(index) >= items.size()) { + return defaultObject; + } else { + return items[index]; + } + } + + /** + * @brief Safely gets a pointer to the element with a given index, returning `nullptr` if the index is outside the range. + * + * @tparam T The type of the array. + * @param items The array. + * @param index The index of the array element to retrieve. + * @return A pointer to the requested element, or `nullptr` if the index is invalid. + */ + template + static const T* getSafe(const std::vector* pItems, int32_t index) { + if (index < 0 || static_cast(index) >= pItems->size()) { + return nullptr; + } else { + return &(*pItems)[index]; + } + } + + /** + * @brief Safely gets a pointer to the element with a given index, returning `nullptr` if the index is outside the range. + * + * @tparam T The type of the array. + * @param items The array. + * @param index The index of the array element to retrieve. + * @return A pointer to the requested element, or `nullptr` if the index is invalid. + */ + template + static T* getSafe(std::vector* pItems, int32_t index) { + if (index < 0 || static_cast(index) >= pItems->size()) { + return nullptr; + } else { + return &(*pItems)[index]; + } + } + }; +} diff --git a/CesiumGltf/include/CesiumGltf/ModelSpec.h b/CesiumGltf/include/CesiumGltf/ModelSpec.h new file mode 100644 index 000000000..3e1c272cf --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/ModelSpec.h @@ -0,0 +1,142 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/Accessor.h" +#include "CesiumGltf/Animation.h" +#include "CesiumGltf/Asset.h" +#include "CesiumGltf/Buffer.h" +#include "CesiumGltf/BufferView.h" +#include "CesiumGltf/Camera.h" +#include "CesiumGltf/ExtensibleObject.h" +#include "CesiumGltf/Image.h" +#include "CesiumGltf/Library.h" +#include "CesiumGltf/Material.h" +#include "CesiumGltf/Mesh.h" +#include "CesiumGltf/Node.h" +#include "CesiumGltf/Sampler.h" +#include "CesiumGltf/Scene.h" +#include "CesiumGltf/Skin.h" +#include "CesiumGltf/Texture.h" +#include +#include +#include + +namespace CesiumGltf { + /** + * @brief The root object for a glTF asset. + */ + struct CESIUMGLTF_API ModelSpec : public ExtensibleObject { + + /** + * @brief Names of glTF extensions used somewhere in this asset. + */ + std::vector extensionsUsed; + + /** + * @brief Names of glTF extensions required to properly load this asset. + */ + std::vector extensionsRequired; + + /** + * @brief An array of accessors. + * + * An accessor is a typed view into a bufferView. + */ + std::vector accessors; + + /** + * @brief An array of keyframe animations. + */ + std::vector animations; + + /** + * @brief Metadata about the glTF asset. + */ + Asset asset; + + /** + * @brief An array of buffers. + * + * A buffer points to binary geometry, animation, or skins. + */ + std::vector buffers; + + /** + * @brief An array of bufferViews. + * + * A bufferView is a view into a buffer generally representing a subset of the buffer. + */ + std::vector bufferViews; + + /** + * @brief An array of cameras. + * + * A camera defines a projection matrix. + */ + std::vector cameras; + + /** + * @brief An array of images. + * + * An image defines data used to create a texture. + */ + std::vector images; + + /** + * @brief An array of materials. + * + * A material defines the appearance of a primitive. + */ + std::vector materials; + + /** + * @brief An array of meshes. + * + * A mesh is a set of primitives to be rendered. + */ + std::vector meshes; + + /** + * @brief An array of nodes. + */ + std::vector nodes; + + /** + * @brief An array of samplers. + * + * A sampler contains properties for texture filtering and wrapping modes. + */ + std::vector samplers; + + /** + * @brief The index of the default scene. + */ + int32_t scene = -1; + + /** + * @brief An array of scenes. + */ + std::vector scenes; + + /** + * @brief An array of skins. + * + * A skin is defined by joints and matrices. + */ + std::vector skins; + + /** + * @brief An array of textures. + */ + std::vector textures; + + private: + /** + * @brief This class is not mean to be instantiated directly. Use {@link Model} instead. + */ + ModelSpec() = default; + friend struct Model; + + }; +} diff --git a/CesiumGltf/include/CesiumGltf/NamedObject.h b/CesiumGltf/include/CesiumGltf/NamedObject.h new file mode 100644 index 000000000..cbfbeff69 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/NamedObject.h @@ -0,0 +1,21 @@ +#pragma once + +#include "CesiumGltf/ExtensibleObject.h" +#include "CesiumGltf/Library.h" +#include + +namespace CesiumGltf { + /** + * @brief The base class for objects in a glTF that have a name. + * + * A named object is also an {@link ExtensibleObject}. + */ + struct CESIUMGLTF_API NamedObject : public ExtensibleObject { + /** + * @brief The user-defined name of this object. + * + * This is not necessarily unique, e.g., an accessor and a buffer could have the same name, or two accessors could even have the same name. + */ + std::string name; + }; +} diff --git a/CesiumGltf/include/CesiumGltf/Node.h b/CesiumGltf/include/CesiumGltf/Node.h new file mode 100644 index 000000000..230f92111 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/Node.h @@ -0,0 +1,64 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/Library.h" +#include "CesiumGltf/NamedObject.h" +#include +#include + +namespace CesiumGltf { + /** + * @brief A node in the node hierarchy. When the node contains `skin`, all `mesh.primitives` must contain `JOINTS_0` and `WEIGHTS_0` attributes. A node can have either a `matrix` or any combination of `translation`/`rotation`/`scale` (TRS) properties. TRS properties are converted to matrices and postmultiplied in the `T * R * S` order to compose the transformation matrix; first the scale is applied to the vertices, then the rotation, and then the translation. If none are provided, the transform is the identity. When a node is targeted for animation (referenced by an animation.channel.target), only TRS properties may be present; `matrix` will not be present. + */ + struct CESIUMGLTF_API Node final : public NamedObject { + + /** + * @brief The index of the camera referenced by this node. + */ + int32_t camera = -1; + + /** + * @brief The indices of this node's children. + */ + std::vector children; + + /** + * @brief The index of the skin referenced by this node. + * + * When a skin is referenced by a node within a scene, all joints used by the skin must belong to the same scene. + */ + int32_t skin = -1; + + /** + * @brief A floating-point 4x4 transformation matrix stored in column-major order. + */ + std::vector matrix = { 1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1 }; + + /** + * @brief The index of the mesh in this node. + */ + int32_t mesh = -1; + + /** + * @brief The node's unit quaternion rotation in the order (x, y, z, w), where w is the scalar. + */ + std::vector rotation = { 0,0,0,1 }; + + /** + * @brief The node's non-uniform scale, given as the scaling factors along the x, y, and z axes. + */ + std::vector scale = { 1,1,1 }; + + /** + * @brief The node's translation along the x, y, and z axes. + */ + std::vector translation = { 0,0,0 }; + + /** + * @brief The weights of the instantiated Morph Target. Number of elements must match number of Morph Targets of used mesh. + */ + std::vector weights; + + }; +} diff --git a/CesiumGltf/include/CesiumGltf/Sampler.h b/CesiumGltf/include/CesiumGltf/Sampler.h new file mode 100644 index 000000000..956e13a7b --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/Sampler.h @@ -0,0 +1,79 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/Library.h" +#include "CesiumGltf/NamedObject.h" +#include + +namespace CesiumGltf { + /** + * @brief Texture sampler properties for filtering and wrapping modes. + */ + struct CESIUMGLTF_API Sampler final : public NamedObject { + enum class MagFilter { + NEAREST = 9728, + + LINEAR = 9729 + }; + + enum class MinFilter { + NEAREST = 9728, + + LINEAR = 9729, + + NEAREST_MIPMAP_NEAREST = 9984, + + LINEAR_MIPMAP_NEAREST = 9985, + + NEAREST_MIPMAP_LINEAR = 9986, + + LINEAR_MIPMAP_LINEAR = 9987 + }; + + enum class WrapS { + CLAMP_TO_EDGE = 33071, + + MIRRORED_REPEAT = 33648, + + REPEAT = 10497 + }; + + enum class WrapT { + CLAMP_TO_EDGE = 33071, + + MIRRORED_REPEAT = 33648, + + REPEAT = 10497 + }; + + /** + * @brief Magnification filter. + * + * Valid values correspond to WebGL enums: `9728` (NEAREST) and `9729` (LINEAR). + */ + std::optional magFilter; + + /** + * @brief Minification filter. + * + * All valid values correspond to WebGL enums. + */ + std::optional minFilter; + + /** + * @brief s wrapping mode. + * + * S (U) wrapping mode. All valid values correspond to WebGL enums. + */ + WrapS wrapS = WrapS(10497); + + /** + * @brief t wrapping mode. + * + * T (V) wrapping mode. All valid values correspond to WebGL enums. + */ + WrapT wrapT = WrapT(10497); + + }; +} diff --git a/CesiumGltf/include/CesiumGltf/Scene.h b/CesiumGltf/include/CesiumGltf/Scene.h new file mode 100644 index 000000000..7ffaa539f --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/Scene.h @@ -0,0 +1,22 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/Library.h" +#include "CesiumGltf/NamedObject.h" +#include +#include + +namespace CesiumGltf { + /** + * @brief The root nodes of a scene. + */ + struct CESIUMGLTF_API Scene final : public NamedObject { + + /** + * @brief The indices of each root node. + */ + std::vector nodes; + + }; +} diff --git a/CesiumGltf/include/CesiumGltf/Skin.h b/CesiumGltf/include/CesiumGltf/Skin.h new file mode 100644 index 000000000..7ed2c2740 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/Skin.h @@ -0,0 +1,36 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/Library.h" +#include "CesiumGltf/NamedObject.h" +#include +#include + +namespace CesiumGltf { + /** + * @brief Joints and matrices defining a skin. + */ + struct CESIUMGLTF_API Skin final : public NamedObject { + + /** + * @brief The index of the accessor containing the floating-point 4x4 inverse-bind matrices. The default is that each matrix is a 4x4 identity matrix, which implies that inverse-bind matrices were pre-applied. + */ + int32_t inverseBindMatrices = -1; + + /** + * @brief The index of the node used as a skeleton root. + * + * The node must be the closest common root of the joints hierarchy or a direct or indirect parent node of the closest common root. + */ + int32_t skeleton = -1; + + /** + * @brief Indices of skeleton nodes, used as joints in this skin. + * + * The array length must be the same as the `count` property of the `inverseBindMatrices` accessor (when defined). + */ + std::vector joints; + + }; +} diff --git a/CesiumGltf/include/CesiumGltf/Texture.h b/CesiumGltf/include/CesiumGltf/Texture.h new file mode 100644 index 000000000..7f1f07177 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/Texture.h @@ -0,0 +1,26 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/Library.h" +#include "CesiumGltf/NamedObject.h" +#include + +namespace CesiumGltf { + /** + * @brief A texture and its sampler. + */ + struct CESIUMGLTF_API Texture final : public NamedObject { + + /** + * @brief The index of the sampler used by this texture. When undefined, a sampler with repeat wrapping and auto filtering should be used. + */ + int32_t sampler = -1; + + /** + * @brief The index of the image used by this texture. When undefined, it is expected that an extension or other mechanism will supply an alternate texture source, otherwise behavior is undefined. + */ + int32_t source = -1; + + }; +} diff --git a/CesiumGltf/include/CesiumGltf/TextureInfo.h b/CesiumGltf/include/CesiumGltf/TextureInfo.h new file mode 100644 index 000000000..eecc20850 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/TextureInfo.h @@ -0,0 +1,28 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/ExtensibleObject.h" +#include "CesiumGltf/Library.h" +#include + +namespace CesiumGltf { + /** + * @brief Reference to a texture. + */ + struct CESIUMGLTF_API TextureInfo : public ExtensibleObject { + + /** + * @brief The index of the texture. + */ + int32_t index = -1; + + /** + * @brief The set index of texture's TEXCOORD attribute used for texture coordinate mapping. + * + * This integer value is used to construct a string in the format `TEXCOORD_` which is a reference to a key in mesh.primitives.attributes (e.g. A value of `0` corresponds to `TEXCOORD_0`). Mesh must have corresponding texture coordinate attributes for the material to be applicable to it. + */ + int64_t texCoord = 0; + + }; +} diff --git a/CesiumGltf/src/Accessor.cpp b/CesiumGltf/src/Accessor.cpp new file mode 100644 index 000000000..6836c03a0 --- /dev/null +++ b/CesiumGltf/src/Accessor.cpp @@ -0,0 +1,66 @@ +#include "CesiumGltf/Accessor.h" +#include "CesiumGltf/Model.h" + +using namespace CesiumGltf; + +/*static*/ int8_t Accessor::computeNumberOfComponents(CesiumGltf::Accessor::Type type) { + switch (type) { + case CesiumGltf::Accessor::Type::SCALAR: + return 1; + case CesiumGltf::Accessor::Type::VEC2: + return 2; + case CesiumGltf::Accessor::Type::VEC3: + return 3; + case CesiumGltf::Accessor::Type::VEC4: + return 4; + case CesiumGltf::Accessor::Type::MAT2: + return 4; + case CesiumGltf::Accessor::Type::MAT3: + return 9; + case CesiumGltf::Accessor::Type::MAT4: + return 16; + default: + return 0; + } +} + +/*static*/ int8_t Accessor::computeByteSizeOfComponent(CesiumGltf::Accessor::ComponentType componentType) { + switch (componentType) { + case CesiumGltf::Accessor::ComponentType::BYTE: + case CesiumGltf::Accessor::ComponentType::UNSIGNED_BYTE: + return 1; + case CesiumGltf::Accessor::ComponentType::SHORT: + case CesiumGltf::Accessor::ComponentType::UNSIGNED_SHORT: + return 2; + case CesiumGltf::Accessor::ComponentType::UNSIGNED_INT: + case CesiumGltf::Accessor::ComponentType::FLOAT: + return 4; + default: + return 0; + } +} + +int8_t Accessor::computeNumberOfComponents() const { + return Accessor::computeNumberOfComponents(this->type); +} + +int8_t Accessor::computeByteSizeOfComponent() const { + return Accessor::computeByteSizeOfComponent(this->componentType); +} + +int64_t Accessor::computeBytesPerVertex() const { + return this->computeByteSizeOfComponent() * this->computeNumberOfComponents(); +} + +int64_t Accessor::computeByteStride(const CesiumGltf::Model& model) const { + const BufferView* pBufferView = Model::getSafe(&model.bufferViews, this->bufferView); + if (!pBufferView) { + return 0; + } + + if (pBufferView->byteStride) { + return pBufferView->byteStride.value(); + } else { + return computeNumberOfComponents(this->type) * computeByteSizeOfComponent(this->componentType); + } +} diff --git a/CesiumGltf/src/JsonValue.cpp b/CesiumGltf/src/JsonValue.cpp new file mode 100644 index 000000000..64bd23379 --- /dev/null +++ b/CesiumGltf/src/JsonValue.cpp @@ -0,0 +1,82 @@ +#include "CesiumGltf/JsonValue.h" + +using namespace CesiumGltf; + +double JsonValue::getNumber(double defaultValue) const { + const double* p = std::get_if(&this->value); + if (p) { + return *p; + } else { + return defaultValue; + } +} + +bool JsonValue::getBool(bool defaultValue) const { + const bool* p = std::get_if(&this->value); + if (p) { + return *p; + } else { + return defaultValue; + } +} + +std::string JsonValue::getString(const std::string& defaultValue) const { + const std::string* p = std::get_if(&this->value); + if (p) { + return *p; + } else { + return defaultValue; + } +} + +const JsonValue* JsonValue::getValueForKey(const std::string& key) const { + const Object* pObject = std::get_if(&this->value); + if (!pObject) { + return nullptr; + } + + auto it = pObject->find(key); + if (it == pObject->end()) { + return nullptr; + } + + return &it->second; +} + +JsonValue* JsonValue::getValueForKey(const std::string& key) { + Object* pObject = std::get_if(&this->value); + if (!pObject) { + return nullptr; + } + + auto it = pObject->find(key); + if (it == pObject->end()) { + return nullptr; + } + + return &it->second; +} + +bool JsonValue::isNull() const { + return std::holds_alternative(this->value); +} + +bool JsonValue::isNumber() const { + return std::holds_alternative(this->value); +} + +bool JsonValue::isBool() const { + return std::holds_alternative(this->value); +} + +bool JsonValue::isString() const { + return std::holds_alternative(this->value); +} + +bool JsonValue::isObject() const { + return std::holds_alternative(this->value); +} + +bool JsonValue::isArray() const { + return std::holds_alternative(this->value); +} diff --git a/CesiumGltf/src/Model.cpp b/CesiumGltf/src/Model.cpp new file mode 100644 index 000000000..98080230d --- /dev/null +++ b/CesiumGltf/src/Model.cpp @@ -0,0 +1,198 @@ +#include "CesiumGltf/Model.h" +#include "CesiumGltf/KHR_draco_mesh_compression.h" +#include + +using namespace CesiumGltf; + +namespace { + template + size_t copyElements(std::vector& to, std::vector& from) { + size_t out = to.size(); + to.resize(out + from.size()); + for (size_t i = 0; i < from.size(); ++i) { + to[out + i] = std::move(from[i]); + } + + return out; + } + + void updateIndex(int32_t& index, size_t offset) { + if (index == -1) { + return; + } + index += int32_t(offset); + } +} + +void Model::merge(Model&& rhs) { + // TODO: we could generate this pretty easily if the glTF JSON schema made + // it clear which index properties refer to which types of objects. + + // Copy all the source data into this instance. + copyElements(this->extensionsUsed, rhs.extensionsUsed); + std::sort(this->extensionsUsed.begin(), this->extensionsUsed.end()); + this->extensionsUsed.erase(std::unique(this->extensionsUsed.begin(), this->extensionsUsed.end()), this->extensionsUsed.end()); + + copyElements(this->extensionsRequired, rhs.extensionsRequired); + std::sort(this->extensionsRequired.begin(), this->extensionsRequired.end()); + this->extensionsRequired.erase(std::unique(this->extensionsRequired.begin(), this->extensionsRequired.end()), this->extensionsRequired.end()); + + size_t firstAccessor = copyElements(this->accessors, rhs.accessors); + size_t firstAnimation = copyElements(this->animations, rhs.animations); + size_t firstBuffer = copyElements(this->buffers, rhs.buffers); + size_t firstBufferView = copyElements(this->bufferViews, rhs.bufferViews); + size_t firstCamera = copyElements(this->cameras, rhs.cameras); + size_t firstImage = copyElements(this->images, rhs.images); + size_t firstMaterial = copyElements(this->materials, rhs.materials); + size_t firstMesh = copyElements(this->meshes, rhs.meshes); + size_t firstNode = copyElements(this->nodes, rhs.nodes); + size_t firstSampler = copyElements(this->samplers, rhs.samplers); + size_t firstScene = copyElements(this->scenes, rhs.scenes); + size_t firstSkin = copyElements(this->skins, rhs.skins); + size_t firstTexture = copyElements(this->textures, rhs.textures); + + // Update the copied indices + for (size_t i = firstAccessor; i < this->accessors.size(); ++i) { + Accessor& accessor = this->accessors[i]; + updateIndex(accessor.bufferView, firstBufferView); + + if (accessor.sparse) { + updateIndex(accessor.sparse.value().indices.bufferView, firstBufferView); + updateIndex(accessor.sparse.value().values.bufferView, firstBufferView); + } + } + + for (size_t i = firstAnimation; i < this->animations.size(); ++i) { + Animation& animation = this->animations[i]; + + for (AnimationChannel& channel : animation.channels) { + updateIndex(channel.sampler, firstSampler); + updateIndex(channel.target.node, firstNode); + } + + for (AnimationSampler& sampler : animation.samplers) { + updateIndex(sampler.input, firstAccessor); + updateIndex(sampler.output, firstAccessor); + } + } + + for (size_t i = firstBufferView; i < this->bufferViews.size(); ++i) { + BufferView& bufferView = this->bufferViews[i]; + updateIndex(bufferView.buffer, firstBuffer); + } + + for (size_t i = firstImage; i < this->images.size(); ++i) { + Image& image = this->images[i]; + updateIndex(image.bufferView, firstBufferView); + } + + for (size_t i = firstMesh; i < this->meshes.size(); ++i) { + Mesh& mesh = this->meshes[i]; + + for (MeshPrimitive& primitive : mesh.primitives) { + updateIndex(primitive.indices, firstAccessor); + updateIndex(primitive.material, firstMaterial); + + for (auto& attribute : primitive.attributes) { + updateIndex(attribute.second, firstAccessor); + } + + for (auto& target : primitive.targets) { + for (auto& displacement : target) { + updateIndex(displacement.second, firstAccessor); + } + } + + KHR_draco_mesh_compression* pDraco = primitive.getExtension(); + if (pDraco) { + updateIndex(pDraco->bufferView, firstBufferView); + } + } + } + + for (size_t i = firstNode; i < this->nodes.size(); ++i) { + Node& node = this->nodes[i]; + + updateIndex(node.camera, firstCamera); + updateIndex(node.skin, firstSkin); + updateIndex(node.mesh, firstMesh); + + for (auto& nodeIndex : node.children) { + updateIndex(nodeIndex, firstNode); + } + } + + for (size_t i = firstScene; i < this->scenes.size(); ++i) { + Scene& currentScene = this->scenes[i]; + for (int32_t& node : currentScene.nodes) { + updateIndex(node, firstNode); + } + } + + for (size_t i = firstSkin; i < this->skins.size(); ++i) { + Skin& skin = this->skins[i]; + + updateIndex(skin.inverseBindMatrices, firstAccessor); + updateIndex(skin.skeleton, firstNode); + + for (int32_t& nodeIndex : skin.joints) { + updateIndex(nodeIndex, firstNode); + } + } + + for (size_t i = firstTexture; i < this->textures.size(); ++i) { + Texture& texture = this->textures[i]; + + updateIndex(texture.sampler, firstSampler); + updateIndex(texture.source, firstImage); + } + + for (size_t i = firstMaterial; i < this->materials.size(); ++i) { + Material& material = this->materials[i]; + + if (material.normalTexture) { + updateIndex(material.normalTexture.value().index, firstTexture); + } + if (material.occlusionTexture) { + updateIndex(material.occlusionTexture.value().index, firstTexture); + } + if (material.pbrMetallicRoughness) { + MaterialPBRMetallicRoughness& pbr = material.pbrMetallicRoughness.value(); + if (pbr.baseColorTexture) { + updateIndex(pbr.baseColorTexture.value().index, firstTexture); + } + if (pbr.metallicRoughnessTexture) { + updateIndex(pbr.metallicRoughnessTexture.value().index, firstTexture); + } + } + if (material.emissiveTexture) { + updateIndex(material.emissiveTexture.value().index, firstTexture); + } + } + + Scene* pThisDefaultScene = Model::getSafe(&this->scenes, this->scene); + Scene* pRhsDefaultScene = Model::getSafe(&this->scenes, rhs.scene + int32_t(firstScene)); + + if (!pThisDefaultScene) { + this->scene = rhs.scene; + updateIndex(this->scene, firstScene); + } else if (pRhsDefaultScene) { + // Create a new default scene that has all the root nodes in + // the default scene of either model. + Scene& newScene = this->scenes.emplace_back(); + + // Refresh the scene pointers potentially invalidated by the above. + pThisDefaultScene = Model::getSafe(&this->scenes, this->scene); + pRhsDefaultScene = Model::getSafe(&this->scenes, rhs.scene + int32_t(firstScene)); + + newScene.nodes = pThisDefaultScene->nodes; + size_t originalNodeCount = newScene.nodes.size(); + newScene.nodes.resize(originalNodeCount + pRhsDefaultScene->nodes.size()); + std::copy(pRhsDefaultScene->nodes.begin(), pRhsDefaultScene->nodes.end(), newScene.nodes.begin() + int64_t(originalNodeCount)); + + // No need to update indices because they've already been updated when + // we copied them from rhs to this. + + this->scene = int32_t(this->scenes.size() - 1); + } +} diff --git a/CesiumGltf/test/TestAccessorView.cpp b/CesiumGltf/test/TestAccessorView.cpp new file mode 100644 index 000000000..454cb567d --- /dev/null +++ b/CesiumGltf/test/TestAccessorView.cpp @@ -0,0 +1,39 @@ +#include "catch2/catch.hpp" +#include "CesiumGltf/Model.h" +#include "CesiumGltf/AccessorView.h" +#include + +TEST_CASE("AccessorView construct and read example") { + auto anyOldFunctionToGetAModel = []() { + CesiumGltf::Model model; + + CesiumGltf::Accessor& accessor = model.accessors.emplace_back(); + accessor.bufferView = 0; + accessor.componentType = CesiumGltf::Accessor::ComponentType::FLOAT; + accessor.type = CesiumGltf::Accessor::Type::VEC3; + accessor.count = 1; + + CesiumGltf::BufferView& bufferView = model.bufferViews.emplace_back(); + bufferView.buffer = 0; + bufferView.byteLength = accessor.count * int64_t(sizeof(float)) * 3; + + CesiumGltf::Buffer& buffer = model.buffers.emplace_back(); + buffer.byteLength = bufferView.byteLength; + buffer.cesium.data.resize(size_t(buffer.byteLength)); + + float* p = reinterpret_cast(buffer.cesium.data.data()); + p[0] = 1.0f; + p[1] = 2.0f; + p[2] = 3.0f; + + return model; + }; + + //! [createFromAccessorAndRead] + CesiumGltf::Model model = anyOldFunctionToGetAModel(); + CesiumGltf::AccessorView positions(model, 0); + glm::vec3 firstPosition = positions[0]; + //! [createFromAccessorAndRead] + + CHECK(firstPosition == glm::vec3(1.0f, 2.0f, 3.0f)); +} diff --git a/CesiumGltfReader/CMakeLists.txt b/CesiumGltfReader/CMakeLists.txt new file mode 100644 index 000000000..7321df6b1 --- /dev/null +++ b/CesiumGltfReader/CMakeLists.txt @@ -0,0 +1,32 @@ +add_library(CesiumGltfReader "") + +configure_cesium_library(CesiumGltfReader) + +file(GLOB SOURCES include/CesiumGltf/*.h src/*.cpp src/*.h generated/*.cpp generated/*.h) +file(GLOB PUBLIC_INCLUDES include/CesiumGltf/*.h) + +target_sources( + CesiumGltfReader + PRIVATE + ${SOURCES} + PUBLIC + ${PUBLIC_INCLUDES} +) + +target_include_directories( + CesiumGltfReader + SYSTEM PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/../extern/rapidjson/include + + # Draco's CMake doesn't use target_include_directories, so we need to manually add draco's include dirs here. + ${CMAKE_CURRENT_SOURCE_DIR}/../extern/draco/src + ${CMAKE_BINARY_DIR} + PUBLIC + include + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/../extern/stb + src + generated +) + +target_link_libraries(CesiumGltfReader PUBLIC CesiumGltf GSL PRIVATE draco modp_b64) diff --git a/CesiumGltfReader/generated/AccessorJsonHandler.cpp b/CesiumGltfReader/generated/AccessorJsonHandler.cpp new file mode 100644 index 000000000..0dfc2b530 --- /dev/null +++ b/CesiumGltfReader/generated/AccessorJsonHandler.cpp @@ -0,0 +1,68 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#include "AccessorJsonHandler.h" +#include "CesiumGltf/Accessor.h" + +#include +#include + +using namespace CesiumGltf; + +void AccessorJsonHandler::reset(IJsonHandler* pParent, Accessor* pObject) { + NamedObjectJsonHandler::reset(pParent, pObject); + this->_pObject = pObject; +} + +Accessor* AccessorJsonHandler::getObject() { + return this->_pObject; +} + +void AccessorJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* AccessorJsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->AccessorKey(str, *this->_pObject); +} + +IJsonHandler* AccessorJsonHandler::AccessorKey(const char* str, Accessor& o) { + using namespace std::string_literals; + + if ("bufferView"s == str) return property("bufferView", this->_bufferView, o.bufferView); + if ("byteOffset"s == str) return property("byteOffset", this->_byteOffset, o.byteOffset); + if ("componentType"s == str) return property("componentType", this->_componentType, o.componentType); + if ("normalized"s == str) return property("normalized", this->_normalized, o.normalized); + if ("count"s == str) return property("count", this->_count, o.count); + if ("type"s == str) return property("type", this->_type, o.type); + if ("max"s == str) return property("max", this->_max, o.max); + if ("min"s == str) return property("min", this->_min, o.min); + if ("sparse"s == str) return property("sparse", this->_sparse, o.sparse); + + return this->NamedObjectKey(str, *this->_pObject); +} + +void AccessorJsonHandler::TypeJsonHandler::reset(IJsonHandler* pParent, Accessor::Type* pEnum) { + JsonHandler::reset(pParent); + this->_pEnum = pEnum; +} + +IJsonHandler* AccessorJsonHandler::TypeJsonHandler::String(const char* str, size_t /*length*/, bool /*copy*/) { + using namespace std::string_literals; + + assert(this->_pEnum); + + if ("SCALAR"s == str) *this->_pEnum = Accessor::Type::SCALAR; + else if ("VEC2"s == str) *this->_pEnum = Accessor::Type::VEC2; + else if ("VEC3"s == str) *this->_pEnum = Accessor::Type::VEC3; + else if ("VEC4"s == str) *this->_pEnum = Accessor::Type::VEC4; + else if ("MAT2"s == str) *this->_pEnum = Accessor::Type::MAT2; + else if ("MAT3"s == str) *this->_pEnum = Accessor::Type::MAT3; + else if ("MAT4"s == str) *this->_pEnum = Accessor::Type::MAT4; + else return nullptr; + + return this->parent(); +} diff --git a/CesiumGltfReader/generated/AccessorJsonHandler.h b/CesiumGltfReader/generated/AccessorJsonHandler.h new file mode 100644 index 000000000..b4410c044 --- /dev/null +++ b/CesiumGltfReader/generated/AccessorJsonHandler.h @@ -0,0 +1,48 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "AccessorSparseJsonHandler.h" +#include "ArrayJsonHandler.h" +#include "BoolJsonHandler.h" +#include "CesiumGltf/Accessor.h" +#include "DoubleJsonHandler.h" +#include "IntegerJsonHandler.h" +#include "NamedObjectJsonHandler.h" + +namespace CesiumGltf { + struct Accessor; + + class AccessorJsonHandler : public NamedObjectJsonHandler { + public: + void reset(IJsonHandler* pHandler, Accessor* pObject); + Accessor* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* AccessorKey(const char* str, Accessor& o); + + private: + class TypeJsonHandler : public JsonHandler { + public: + void reset(IJsonHandler* pParent, Accessor::Type* pEnum); + virtual IJsonHandler* String(const char* str, size_t length, bool copy) override; + + private: + Accessor::Type* _pEnum = nullptr; + }; + + Accessor* _pObject; + IntegerJsonHandler _bufferView; + IntegerJsonHandler _byteOffset; + IntegerJsonHandler _componentType; + BoolJsonHandler _normalized; + IntegerJsonHandler _count; + TypeJsonHandler _type; + ArrayJsonHandler _max; + ArrayJsonHandler _min; + AccessorSparseJsonHandler _sparse; + }; +} diff --git a/CesiumGltfReader/generated/AccessorSparseIndicesJsonHandler.cpp b/CesiumGltfReader/generated/AccessorSparseIndicesJsonHandler.cpp new file mode 100644 index 000000000..76b31df82 --- /dev/null +++ b/CesiumGltfReader/generated/AccessorSparseIndicesJsonHandler.cpp @@ -0,0 +1,40 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#include "AccessorSparseIndicesJsonHandler.h" +#include "CesiumGltf/AccessorSparseIndices.h" + +#include +#include + +using namespace CesiumGltf; + +void AccessorSparseIndicesJsonHandler::reset(IJsonHandler* pParent, AccessorSparseIndices* pObject) { + ExtensibleObjectJsonHandler::reset(pParent, pObject); + this->_pObject = pObject; +} + +AccessorSparseIndices* AccessorSparseIndicesJsonHandler::getObject() { + return this->_pObject; +} + +void AccessorSparseIndicesJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* AccessorSparseIndicesJsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->AccessorSparseIndicesKey(str, *this->_pObject); +} + +IJsonHandler* AccessorSparseIndicesJsonHandler::AccessorSparseIndicesKey(const char* str, AccessorSparseIndices& o) { + using namespace std::string_literals; + + if ("bufferView"s == str) return property("bufferView", this->_bufferView, o.bufferView); + if ("byteOffset"s == str) return property("byteOffset", this->_byteOffset, o.byteOffset); + if ("componentType"s == str) return property("componentType", this->_componentType, o.componentType); + + return this->ExtensibleObjectKey(str, *this->_pObject); +} diff --git a/CesiumGltfReader/generated/AccessorSparseIndicesJsonHandler.h b/CesiumGltfReader/generated/AccessorSparseIndicesJsonHandler.h new file mode 100644 index 000000000..aaaa7f3b7 --- /dev/null +++ b/CesiumGltfReader/generated/AccessorSparseIndicesJsonHandler.h @@ -0,0 +1,30 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/AccessorSparseIndices.h" +#include "ExtensibleObjectJsonHandler.h" +#include "IntegerJsonHandler.h" + +namespace CesiumGltf { + struct AccessorSparseIndices; + + class AccessorSparseIndicesJsonHandler : public ExtensibleObjectJsonHandler { + public: + void reset(IJsonHandler* pHandler, AccessorSparseIndices* pObject); + AccessorSparseIndices* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* AccessorSparseIndicesKey(const char* str, AccessorSparseIndices& o); + + private: + + AccessorSparseIndices* _pObject; + IntegerJsonHandler _bufferView; + IntegerJsonHandler _byteOffset; + IntegerJsonHandler _componentType; + }; +} diff --git a/CesiumGltfReader/generated/AccessorSparseJsonHandler.cpp b/CesiumGltfReader/generated/AccessorSparseJsonHandler.cpp new file mode 100644 index 000000000..dd6c756e3 --- /dev/null +++ b/CesiumGltfReader/generated/AccessorSparseJsonHandler.cpp @@ -0,0 +1,40 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#include "AccessorSparseJsonHandler.h" +#include "CesiumGltf/AccessorSparse.h" + +#include +#include + +using namespace CesiumGltf; + +void AccessorSparseJsonHandler::reset(IJsonHandler* pParent, AccessorSparse* pObject) { + ExtensibleObjectJsonHandler::reset(pParent, pObject); + this->_pObject = pObject; +} + +AccessorSparse* AccessorSparseJsonHandler::getObject() { + return this->_pObject; +} + +void AccessorSparseJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* AccessorSparseJsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->AccessorSparseKey(str, *this->_pObject); +} + +IJsonHandler* AccessorSparseJsonHandler::AccessorSparseKey(const char* str, AccessorSparse& o) { + using namespace std::string_literals; + + if ("count"s == str) return property("count", this->_count, o.count); + if ("indices"s == str) return property("indices", this->_indices, o.indices); + if ("values"s == str) return property("values", this->_values, o.values); + + return this->ExtensibleObjectKey(str, *this->_pObject); +} diff --git a/CesiumGltfReader/generated/AccessorSparseJsonHandler.h b/CesiumGltfReader/generated/AccessorSparseJsonHandler.h new file mode 100644 index 000000000..e39ae4097 --- /dev/null +++ b/CesiumGltfReader/generated/AccessorSparseJsonHandler.h @@ -0,0 +1,31 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "AccessorSparseIndicesJsonHandler.h" +#include "AccessorSparseValuesJsonHandler.h" +#include "ExtensibleObjectJsonHandler.h" +#include "IntegerJsonHandler.h" + +namespace CesiumGltf { + struct AccessorSparse; + + class AccessorSparseJsonHandler : public ExtensibleObjectJsonHandler { + public: + void reset(IJsonHandler* pHandler, AccessorSparse* pObject); + AccessorSparse* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* AccessorSparseKey(const char* str, AccessorSparse& o); + + private: + + AccessorSparse* _pObject; + IntegerJsonHandler _count; + AccessorSparseIndicesJsonHandler _indices; + AccessorSparseValuesJsonHandler _values; + }; +} diff --git a/CesiumGltfReader/generated/AccessorSparseValuesJsonHandler.cpp b/CesiumGltfReader/generated/AccessorSparseValuesJsonHandler.cpp new file mode 100644 index 000000000..460922bfa --- /dev/null +++ b/CesiumGltfReader/generated/AccessorSparseValuesJsonHandler.cpp @@ -0,0 +1,39 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#include "AccessorSparseValuesJsonHandler.h" +#include "CesiumGltf/AccessorSparseValues.h" + +#include +#include + +using namespace CesiumGltf; + +void AccessorSparseValuesJsonHandler::reset(IJsonHandler* pParent, AccessorSparseValues* pObject) { + ExtensibleObjectJsonHandler::reset(pParent, pObject); + this->_pObject = pObject; +} + +AccessorSparseValues* AccessorSparseValuesJsonHandler::getObject() { + return this->_pObject; +} + +void AccessorSparseValuesJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* AccessorSparseValuesJsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->AccessorSparseValuesKey(str, *this->_pObject); +} + +IJsonHandler* AccessorSparseValuesJsonHandler::AccessorSparseValuesKey(const char* str, AccessorSparseValues& o) { + using namespace std::string_literals; + + if ("bufferView"s == str) return property("bufferView", this->_bufferView, o.bufferView); + if ("byteOffset"s == str) return property("byteOffset", this->_byteOffset, o.byteOffset); + + return this->ExtensibleObjectKey(str, *this->_pObject); +} diff --git a/CesiumGltfReader/generated/AccessorSparseValuesJsonHandler.h b/CesiumGltfReader/generated/AccessorSparseValuesJsonHandler.h new file mode 100644 index 000000000..3b1a5e4f6 --- /dev/null +++ b/CesiumGltfReader/generated/AccessorSparseValuesJsonHandler.h @@ -0,0 +1,28 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "ExtensibleObjectJsonHandler.h" +#include "IntegerJsonHandler.h" + +namespace CesiumGltf { + struct AccessorSparseValues; + + class AccessorSparseValuesJsonHandler : public ExtensibleObjectJsonHandler { + public: + void reset(IJsonHandler* pHandler, AccessorSparseValues* pObject); + AccessorSparseValues* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* AccessorSparseValuesKey(const char* str, AccessorSparseValues& o); + + private: + + AccessorSparseValues* _pObject; + IntegerJsonHandler _bufferView; + IntegerJsonHandler _byteOffset; + }; +} diff --git a/CesiumGltfReader/generated/AnimationChannelJsonHandler.cpp b/CesiumGltfReader/generated/AnimationChannelJsonHandler.cpp new file mode 100644 index 000000000..733852867 --- /dev/null +++ b/CesiumGltfReader/generated/AnimationChannelJsonHandler.cpp @@ -0,0 +1,39 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#include "AnimationChannelJsonHandler.h" +#include "CesiumGltf/AnimationChannel.h" + +#include +#include + +using namespace CesiumGltf; + +void AnimationChannelJsonHandler::reset(IJsonHandler* pParent, AnimationChannel* pObject) { + ExtensibleObjectJsonHandler::reset(pParent, pObject); + this->_pObject = pObject; +} + +AnimationChannel* AnimationChannelJsonHandler::getObject() { + return this->_pObject; +} + +void AnimationChannelJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* AnimationChannelJsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->AnimationChannelKey(str, *this->_pObject); +} + +IJsonHandler* AnimationChannelJsonHandler::AnimationChannelKey(const char* str, AnimationChannel& o) { + using namespace std::string_literals; + + if ("sampler"s == str) return property("sampler", this->_sampler, o.sampler); + if ("target"s == str) return property("target", this->_target, o.target); + + return this->ExtensibleObjectKey(str, *this->_pObject); +} diff --git a/CesiumGltfReader/generated/AnimationChannelJsonHandler.h b/CesiumGltfReader/generated/AnimationChannelJsonHandler.h new file mode 100644 index 000000000..ad4cecac2 --- /dev/null +++ b/CesiumGltfReader/generated/AnimationChannelJsonHandler.h @@ -0,0 +1,29 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "AnimationChannelTargetJsonHandler.h" +#include "ExtensibleObjectJsonHandler.h" +#include "IntegerJsonHandler.h" + +namespace CesiumGltf { + struct AnimationChannel; + + class AnimationChannelJsonHandler : public ExtensibleObjectJsonHandler { + public: + void reset(IJsonHandler* pHandler, AnimationChannel* pObject); + AnimationChannel* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* AnimationChannelKey(const char* str, AnimationChannel& o); + + private: + + AnimationChannel* _pObject; + IntegerJsonHandler _sampler; + AnimationChannelTargetJsonHandler _target; + }; +} diff --git a/CesiumGltfReader/generated/AnimationChannelTargetJsonHandler.cpp b/CesiumGltfReader/generated/AnimationChannelTargetJsonHandler.cpp new file mode 100644 index 000000000..4dc419960 --- /dev/null +++ b/CesiumGltfReader/generated/AnimationChannelTargetJsonHandler.cpp @@ -0,0 +1,58 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#include "AnimationChannelTargetJsonHandler.h" +#include "CesiumGltf/AnimationChannelTarget.h" + +#include +#include + +using namespace CesiumGltf; + +void AnimationChannelTargetJsonHandler::reset(IJsonHandler* pParent, AnimationChannelTarget* pObject) { + ExtensibleObjectJsonHandler::reset(pParent, pObject); + this->_pObject = pObject; +} + +AnimationChannelTarget* AnimationChannelTargetJsonHandler::getObject() { + return this->_pObject; +} + +void AnimationChannelTargetJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* AnimationChannelTargetJsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->AnimationChannelTargetKey(str, *this->_pObject); +} + +IJsonHandler* AnimationChannelTargetJsonHandler::AnimationChannelTargetKey(const char* str, AnimationChannelTarget& o) { + using namespace std::string_literals; + + if ("node"s == str) return property("node", this->_node, o.node); + if ("path"s == str) return property("path", this->_path, o.path); + + return this->ExtensibleObjectKey(str, *this->_pObject); +} + +void AnimationChannelTargetJsonHandler::PathJsonHandler::reset(IJsonHandler* pParent, AnimationChannelTarget::Path* pEnum) { + JsonHandler::reset(pParent); + this->_pEnum = pEnum; +} + +IJsonHandler* AnimationChannelTargetJsonHandler::PathJsonHandler::String(const char* str, size_t /*length*/, bool /*copy*/) { + using namespace std::string_literals; + + assert(this->_pEnum); + + if ("translation"s == str) *this->_pEnum = AnimationChannelTarget::Path::translation; + else if ("rotation"s == str) *this->_pEnum = AnimationChannelTarget::Path::rotation; + else if ("scale"s == str) *this->_pEnum = AnimationChannelTarget::Path::scale; + else if ("weights"s == str) *this->_pEnum = AnimationChannelTarget::Path::weights; + else return nullptr; + + return this->parent(); +} diff --git a/CesiumGltfReader/generated/AnimationChannelTargetJsonHandler.h b/CesiumGltfReader/generated/AnimationChannelTargetJsonHandler.h new file mode 100644 index 000000000..ee7f709fa --- /dev/null +++ b/CesiumGltfReader/generated/AnimationChannelTargetJsonHandler.h @@ -0,0 +1,37 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/AnimationChannelTarget.h" +#include "ExtensibleObjectJsonHandler.h" +#include "IntegerJsonHandler.h" + +namespace CesiumGltf { + struct AnimationChannelTarget; + + class AnimationChannelTargetJsonHandler : public ExtensibleObjectJsonHandler { + public: + void reset(IJsonHandler* pHandler, AnimationChannelTarget* pObject); + AnimationChannelTarget* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* AnimationChannelTargetKey(const char* str, AnimationChannelTarget& o); + + private: + class PathJsonHandler : public JsonHandler { + public: + void reset(IJsonHandler* pParent, AnimationChannelTarget::Path* pEnum); + virtual IJsonHandler* String(const char* str, size_t length, bool copy) override; + + private: + AnimationChannelTarget::Path* _pEnum = nullptr; + }; + + AnimationChannelTarget* _pObject; + IntegerJsonHandler _node; + PathJsonHandler _path; + }; +} diff --git a/CesiumGltfReader/generated/AnimationJsonHandler.cpp b/CesiumGltfReader/generated/AnimationJsonHandler.cpp new file mode 100644 index 000000000..800ff7726 --- /dev/null +++ b/CesiumGltfReader/generated/AnimationJsonHandler.cpp @@ -0,0 +1,39 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#include "AnimationJsonHandler.h" +#include "CesiumGltf/Animation.h" + +#include +#include + +using namespace CesiumGltf; + +void AnimationJsonHandler::reset(IJsonHandler* pParent, Animation* pObject) { + NamedObjectJsonHandler::reset(pParent, pObject); + this->_pObject = pObject; +} + +Animation* AnimationJsonHandler::getObject() { + return this->_pObject; +} + +void AnimationJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* AnimationJsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->AnimationKey(str, *this->_pObject); +} + +IJsonHandler* AnimationJsonHandler::AnimationKey(const char* str, Animation& o) { + using namespace std::string_literals; + + if ("channels"s == str) return property("channels", this->_channels, o.channels); + if ("samplers"s == str) return property("samplers", this->_samplers, o.samplers); + + return this->NamedObjectKey(str, *this->_pObject); +} diff --git a/CesiumGltfReader/generated/AnimationJsonHandler.h b/CesiumGltfReader/generated/AnimationJsonHandler.h new file mode 100644 index 000000000..2f5bb3d72 --- /dev/null +++ b/CesiumGltfReader/generated/AnimationJsonHandler.h @@ -0,0 +1,30 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "AnimationChannelJsonHandler.h" +#include "AnimationSamplerJsonHandler.h" +#include "ArrayJsonHandler.h" +#include "NamedObjectJsonHandler.h" + +namespace CesiumGltf { + struct Animation; + + class AnimationJsonHandler : public NamedObjectJsonHandler { + public: + void reset(IJsonHandler* pHandler, Animation* pObject); + Animation* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* AnimationKey(const char* str, Animation& o); + + private: + + Animation* _pObject; + ArrayJsonHandler _channels; + ArrayJsonHandler _samplers; + }; +} diff --git a/CesiumGltfReader/generated/AnimationSamplerJsonHandler.cpp b/CesiumGltfReader/generated/AnimationSamplerJsonHandler.cpp new file mode 100644 index 000000000..1d13d31c1 --- /dev/null +++ b/CesiumGltfReader/generated/AnimationSamplerJsonHandler.cpp @@ -0,0 +1,58 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#include "AnimationSamplerJsonHandler.h" +#include "CesiumGltf/AnimationSampler.h" + +#include +#include + +using namespace CesiumGltf; + +void AnimationSamplerJsonHandler::reset(IJsonHandler* pParent, AnimationSampler* pObject) { + ExtensibleObjectJsonHandler::reset(pParent, pObject); + this->_pObject = pObject; +} + +AnimationSampler* AnimationSamplerJsonHandler::getObject() { + return this->_pObject; +} + +void AnimationSamplerJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* AnimationSamplerJsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->AnimationSamplerKey(str, *this->_pObject); +} + +IJsonHandler* AnimationSamplerJsonHandler::AnimationSamplerKey(const char* str, AnimationSampler& o) { + using namespace std::string_literals; + + if ("input"s == str) return property("input", this->_input, o.input); + if ("interpolation"s == str) return property("interpolation", this->_interpolation, o.interpolation); + if ("output"s == str) return property("output", this->_output, o.output); + + return this->ExtensibleObjectKey(str, *this->_pObject); +} + +void AnimationSamplerJsonHandler::InterpolationJsonHandler::reset(IJsonHandler* pParent, AnimationSampler::Interpolation* pEnum) { + JsonHandler::reset(pParent); + this->_pEnum = pEnum; +} + +IJsonHandler* AnimationSamplerJsonHandler::InterpolationJsonHandler::String(const char* str, size_t /*length*/, bool /*copy*/) { + using namespace std::string_literals; + + assert(this->_pEnum); + + if ("LINEAR"s == str) *this->_pEnum = AnimationSampler::Interpolation::LINEAR; + else if ("STEP"s == str) *this->_pEnum = AnimationSampler::Interpolation::STEP; + else if ("CUBICSPLINE"s == str) *this->_pEnum = AnimationSampler::Interpolation::CUBICSPLINE; + else return nullptr; + + return this->parent(); +} diff --git a/CesiumGltfReader/generated/AnimationSamplerJsonHandler.h b/CesiumGltfReader/generated/AnimationSamplerJsonHandler.h new file mode 100644 index 000000000..e3ab52166 --- /dev/null +++ b/CesiumGltfReader/generated/AnimationSamplerJsonHandler.h @@ -0,0 +1,38 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/AnimationSampler.h" +#include "ExtensibleObjectJsonHandler.h" +#include "IntegerJsonHandler.h" + +namespace CesiumGltf { + struct AnimationSampler; + + class AnimationSamplerJsonHandler : public ExtensibleObjectJsonHandler { + public: + void reset(IJsonHandler* pHandler, AnimationSampler* pObject); + AnimationSampler* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* AnimationSamplerKey(const char* str, AnimationSampler& o); + + private: + class InterpolationJsonHandler : public JsonHandler { + public: + void reset(IJsonHandler* pParent, AnimationSampler::Interpolation* pEnum); + virtual IJsonHandler* String(const char* str, size_t length, bool copy) override; + + private: + AnimationSampler::Interpolation* _pEnum = nullptr; + }; + + AnimationSampler* _pObject; + IntegerJsonHandler _input; + InterpolationJsonHandler _interpolation; + IntegerJsonHandler _output; + }; +} diff --git a/CesiumGltfReader/generated/AssetJsonHandler.cpp b/CesiumGltfReader/generated/AssetJsonHandler.cpp new file mode 100644 index 000000000..ef5191038 --- /dev/null +++ b/CesiumGltfReader/generated/AssetJsonHandler.cpp @@ -0,0 +1,41 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#include "AssetJsonHandler.h" +#include "CesiumGltf/Asset.h" + +#include +#include + +using namespace CesiumGltf; + +void AssetJsonHandler::reset(IJsonHandler* pParent, Asset* pObject) { + ExtensibleObjectJsonHandler::reset(pParent, pObject); + this->_pObject = pObject; +} + +Asset* AssetJsonHandler::getObject() { + return this->_pObject; +} + +void AssetJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* AssetJsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->AssetKey(str, *this->_pObject); +} + +IJsonHandler* AssetJsonHandler::AssetKey(const char* str, Asset& o) { + using namespace std::string_literals; + + if ("copyright"s == str) return property("copyright", this->_copyright, o.copyright); + if ("generator"s == str) return property("generator", this->_generator, o.generator); + if ("version"s == str) return property("version", this->_version, o.version); + if ("minVersion"s == str) return property("minVersion", this->_minVersion, o.minVersion); + + return this->ExtensibleObjectKey(str, *this->_pObject); +} diff --git a/CesiumGltfReader/generated/AssetJsonHandler.h b/CesiumGltfReader/generated/AssetJsonHandler.h new file mode 100644 index 000000000..81cca28a7 --- /dev/null +++ b/CesiumGltfReader/generated/AssetJsonHandler.h @@ -0,0 +1,30 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "ExtensibleObjectJsonHandler.h" +#include "StringJsonHandler.h" + +namespace CesiumGltf { + struct Asset; + + class AssetJsonHandler : public ExtensibleObjectJsonHandler { + public: + void reset(IJsonHandler* pHandler, Asset* pObject); + Asset* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* AssetKey(const char* str, Asset& o); + + private: + + Asset* _pObject; + StringJsonHandler _copyright; + StringJsonHandler _generator; + StringJsonHandler _version; + StringJsonHandler _minVersion; + }; +} diff --git a/CesiumGltfReader/generated/BufferJsonHandler.cpp b/CesiumGltfReader/generated/BufferJsonHandler.cpp new file mode 100644 index 000000000..fd0e25d48 --- /dev/null +++ b/CesiumGltfReader/generated/BufferJsonHandler.cpp @@ -0,0 +1,39 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#include "BufferJsonHandler.h" +#include "CesiumGltf/Buffer.h" + +#include +#include + +using namespace CesiumGltf; + +void BufferJsonHandler::reset(IJsonHandler* pParent, Buffer* pObject) { + NamedObjectJsonHandler::reset(pParent, pObject); + this->_pObject = pObject; +} + +Buffer* BufferJsonHandler::getObject() { + return this->_pObject; +} + +void BufferJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* BufferJsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->BufferKey(str, *this->_pObject); +} + +IJsonHandler* BufferJsonHandler::BufferKey(const char* str, Buffer& o) { + using namespace std::string_literals; + + if ("uri"s == str) return property("uri", this->_uri, o.uri); + if ("byteLength"s == str) return property("byteLength", this->_byteLength, o.byteLength); + + return this->NamedObjectKey(str, *this->_pObject); +} diff --git a/CesiumGltfReader/generated/BufferJsonHandler.h b/CesiumGltfReader/generated/BufferJsonHandler.h new file mode 100644 index 000000000..875ee1afa --- /dev/null +++ b/CesiumGltfReader/generated/BufferJsonHandler.h @@ -0,0 +1,29 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "IntegerJsonHandler.h" +#include "NamedObjectJsonHandler.h" +#include "StringJsonHandler.h" + +namespace CesiumGltf { + struct Buffer; + + class BufferJsonHandler : public NamedObjectJsonHandler { + public: + void reset(IJsonHandler* pHandler, Buffer* pObject); + Buffer* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* BufferKey(const char* str, Buffer& o); + + private: + + Buffer* _pObject; + StringJsonHandler _uri; + IntegerJsonHandler _byteLength; + }; +} diff --git a/CesiumGltfReader/generated/BufferViewJsonHandler.cpp b/CesiumGltfReader/generated/BufferViewJsonHandler.cpp new file mode 100644 index 000000000..e794f862a --- /dev/null +++ b/CesiumGltfReader/generated/BufferViewJsonHandler.cpp @@ -0,0 +1,42 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#include "BufferViewJsonHandler.h" +#include "CesiumGltf/BufferView.h" + +#include +#include + +using namespace CesiumGltf; + +void BufferViewJsonHandler::reset(IJsonHandler* pParent, BufferView* pObject) { + NamedObjectJsonHandler::reset(pParent, pObject); + this->_pObject = pObject; +} + +BufferView* BufferViewJsonHandler::getObject() { + return this->_pObject; +} + +void BufferViewJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* BufferViewJsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->BufferViewKey(str, *this->_pObject); +} + +IJsonHandler* BufferViewJsonHandler::BufferViewKey(const char* str, BufferView& o) { + using namespace std::string_literals; + + if ("buffer"s == str) return property("buffer", this->_buffer, o.buffer); + if ("byteOffset"s == str) return property("byteOffset", this->_byteOffset, o.byteOffset); + if ("byteLength"s == str) return property("byteLength", this->_byteLength, o.byteLength); + if ("byteStride"s == str) return property("byteStride", this->_byteStride, o.byteStride); + if ("target"s == str) return property("target", this->_target, o.target); + + return this->NamedObjectKey(str, *this->_pObject); +} diff --git a/CesiumGltfReader/generated/BufferViewJsonHandler.h b/CesiumGltfReader/generated/BufferViewJsonHandler.h new file mode 100644 index 000000000..ef53a355c --- /dev/null +++ b/CesiumGltfReader/generated/BufferViewJsonHandler.h @@ -0,0 +1,32 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/BufferView.h" +#include "IntegerJsonHandler.h" +#include "NamedObjectJsonHandler.h" + +namespace CesiumGltf { + struct BufferView; + + class BufferViewJsonHandler : public NamedObjectJsonHandler { + public: + void reset(IJsonHandler* pHandler, BufferView* pObject); + BufferView* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* BufferViewKey(const char* str, BufferView& o); + + private: + + BufferView* _pObject; + IntegerJsonHandler _buffer; + IntegerJsonHandler _byteOffset; + IntegerJsonHandler _byteLength; + IntegerJsonHandler _byteStride; + IntegerJsonHandler _target; + }; +} diff --git a/CesiumGltfReader/generated/CameraJsonHandler.cpp b/CesiumGltfReader/generated/CameraJsonHandler.cpp new file mode 100644 index 000000000..be6085f8b --- /dev/null +++ b/CesiumGltfReader/generated/CameraJsonHandler.cpp @@ -0,0 +1,57 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#include "CameraJsonHandler.h" +#include "CesiumGltf/Camera.h" + +#include +#include + +using namespace CesiumGltf; + +void CameraJsonHandler::reset(IJsonHandler* pParent, Camera* pObject) { + NamedObjectJsonHandler::reset(pParent, pObject); + this->_pObject = pObject; +} + +Camera* CameraJsonHandler::getObject() { + return this->_pObject; +} + +void CameraJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* CameraJsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->CameraKey(str, *this->_pObject); +} + +IJsonHandler* CameraJsonHandler::CameraKey(const char* str, Camera& o) { + using namespace std::string_literals; + + if ("orthographic"s == str) return property("orthographic", this->_orthographic, o.orthographic); + if ("perspective"s == str) return property("perspective", this->_perspective, o.perspective); + if ("type"s == str) return property("type", this->_type, o.type); + + return this->NamedObjectKey(str, *this->_pObject); +} + +void CameraJsonHandler::TypeJsonHandler::reset(IJsonHandler* pParent, Camera::Type* pEnum) { + JsonHandler::reset(pParent); + this->_pEnum = pEnum; +} + +IJsonHandler* CameraJsonHandler::TypeJsonHandler::String(const char* str, size_t /*length*/, bool /*copy*/) { + using namespace std::string_literals; + + assert(this->_pEnum); + + if ("perspective"s == str) *this->_pEnum = Camera::Type::perspective; + else if ("orthographic"s == str) *this->_pEnum = Camera::Type::orthographic; + else return nullptr; + + return this->parent(); +} diff --git a/CesiumGltfReader/generated/CameraJsonHandler.h b/CesiumGltfReader/generated/CameraJsonHandler.h new file mode 100644 index 000000000..c32588876 --- /dev/null +++ b/CesiumGltfReader/generated/CameraJsonHandler.h @@ -0,0 +1,39 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CameraOrthographicJsonHandler.h" +#include "CameraPerspectiveJsonHandler.h" +#include "CesiumGltf/Camera.h" +#include "NamedObjectJsonHandler.h" + +namespace CesiumGltf { + struct Camera; + + class CameraJsonHandler : public NamedObjectJsonHandler { + public: + void reset(IJsonHandler* pHandler, Camera* pObject); + Camera* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* CameraKey(const char* str, Camera& o); + + private: + class TypeJsonHandler : public JsonHandler { + public: + void reset(IJsonHandler* pParent, Camera::Type* pEnum); + virtual IJsonHandler* String(const char* str, size_t length, bool copy) override; + + private: + Camera::Type* _pEnum = nullptr; + }; + + Camera* _pObject; + CameraOrthographicJsonHandler _orthographic; + CameraPerspectiveJsonHandler _perspective; + TypeJsonHandler _type; + }; +} diff --git a/CesiumGltfReader/generated/CameraOrthographicJsonHandler.cpp b/CesiumGltfReader/generated/CameraOrthographicJsonHandler.cpp new file mode 100644 index 000000000..69a93076e --- /dev/null +++ b/CesiumGltfReader/generated/CameraOrthographicJsonHandler.cpp @@ -0,0 +1,41 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#include "CameraOrthographicJsonHandler.h" +#include "CesiumGltf/CameraOrthographic.h" + +#include +#include + +using namespace CesiumGltf; + +void CameraOrthographicJsonHandler::reset(IJsonHandler* pParent, CameraOrthographic* pObject) { + ExtensibleObjectJsonHandler::reset(pParent, pObject); + this->_pObject = pObject; +} + +CameraOrthographic* CameraOrthographicJsonHandler::getObject() { + return this->_pObject; +} + +void CameraOrthographicJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* CameraOrthographicJsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->CameraOrthographicKey(str, *this->_pObject); +} + +IJsonHandler* CameraOrthographicJsonHandler::CameraOrthographicKey(const char* str, CameraOrthographic& o) { + using namespace std::string_literals; + + if ("xmag"s == str) return property("xmag", this->_xmag, o.xmag); + if ("ymag"s == str) return property("ymag", this->_ymag, o.ymag); + if ("zfar"s == str) return property("zfar", this->_zfar, o.zfar); + if ("znear"s == str) return property("znear", this->_znear, o.znear); + + return this->ExtensibleObjectKey(str, *this->_pObject); +} diff --git a/CesiumGltfReader/generated/CameraOrthographicJsonHandler.h b/CesiumGltfReader/generated/CameraOrthographicJsonHandler.h new file mode 100644 index 000000000..40fff6b1e --- /dev/null +++ b/CesiumGltfReader/generated/CameraOrthographicJsonHandler.h @@ -0,0 +1,30 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "DoubleJsonHandler.h" +#include "ExtensibleObjectJsonHandler.h" + +namespace CesiumGltf { + struct CameraOrthographic; + + class CameraOrthographicJsonHandler : public ExtensibleObjectJsonHandler { + public: + void reset(IJsonHandler* pHandler, CameraOrthographic* pObject); + CameraOrthographic* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* CameraOrthographicKey(const char* str, CameraOrthographic& o); + + private: + + CameraOrthographic* _pObject; + DoubleJsonHandler _xmag; + DoubleJsonHandler _ymag; + DoubleJsonHandler _zfar; + DoubleJsonHandler _znear; + }; +} diff --git a/CesiumGltfReader/generated/CameraPerspectiveJsonHandler.cpp b/CesiumGltfReader/generated/CameraPerspectiveJsonHandler.cpp new file mode 100644 index 000000000..4063f141c --- /dev/null +++ b/CesiumGltfReader/generated/CameraPerspectiveJsonHandler.cpp @@ -0,0 +1,41 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#include "CameraPerspectiveJsonHandler.h" +#include "CesiumGltf/CameraPerspective.h" + +#include +#include + +using namespace CesiumGltf; + +void CameraPerspectiveJsonHandler::reset(IJsonHandler* pParent, CameraPerspective* pObject) { + ExtensibleObjectJsonHandler::reset(pParent, pObject); + this->_pObject = pObject; +} + +CameraPerspective* CameraPerspectiveJsonHandler::getObject() { + return this->_pObject; +} + +void CameraPerspectiveJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* CameraPerspectiveJsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->CameraPerspectiveKey(str, *this->_pObject); +} + +IJsonHandler* CameraPerspectiveJsonHandler::CameraPerspectiveKey(const char* str, CameraPerspective& o) { + using namespace std::string_literals; + + if ("aspectRatio"s == str) return property("aspectRatio", this->_aspectRatio, o.aspectRatio); + if ("yfov"s == str) return property("yfov", this->_yfov, o.yfov); + if ("zfar"s == str) return property("zfar", this->_zfar, o.zfar); + if ("znear"s == str) return property("znear", this->_znear, o.znear); + + return this->ExtensibleObjectKey(str, *this->_pObject); +} diff --git a/CesiumGltfReader/generated/CameraPerspectiveJsonHandler.h b/CesiumGltfReader/generated/CameraPerspectiveJsonHandler.h new file mode 100644 index 000000000..155ae50ba --- /dev/null +++ b/CesiumGltfReader/generated/CameraPerspectiveJsonHandler.h @@ -0,0 +1,30 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "DoubleJsonHandler.h" +#include "ExtensibleObjectJsonHandler.h" + +namespace CesiumGltf { + struct CameraPerspective; + + class CameraPerspectiveJsonHandler : public ExtensibleObjectJsonHandler { + public: + void reset(IJsonHandler* pHandler, CameraPerspective* pObject); + CameraPerspective* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* CameraPerspectiveKey(const char* str, CameraPerspective& o); + + private: + + CameraPerspective* _pObject; + DoubleJsonHandler _aspectRatio; + DoubleJsonHandler _yfov; + DoubleJsonHandler _zfar; + DoubleJsonHandler _znear; + }; +} diff --git a/CesiumGltfReader/generated/ImageJsonHandler.cpp b/CesiumGltfReader/generated/ImageJsonHandler.cpp new file mode 100644 index 000000000..4eccf7046 --- /dev/null +++ b/CesiumGltfReader/generated/ImageJsonHandler.cpp @@ -0,0 +1,57 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#include "ImageJsonHandler.h" +#include "CesiumGltf/Image.h" + +#include +#include + +using namespace CesiumGltf; + +void ImageJsonHandler::reset(IJsonHandler* pParent, Image* pObject) { + NamedObjectJsonHandler::reset(pParent, pObject); + this->_pObject = pObject; +} + +Image* ImageJsonHandler::getObject() { + return this->_pObject; +} + +void ImageJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* ImageJsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->ImageKey(str, *this->_pObject); +} + +IJsonHandler* ImageJsonHandler::ImageKey(const char* str, Image& o) { + using namespace std::string_literals; + + if ("uri"s == str) return property("uri", this->_uri, o.uri); + if ("mimeType"s == str) return property("mimeType", this->_mimeType, o.mimeType); + if ("bufferView"s == str) return property("bufferView", this->_bufferView, o.bufferView); + + return this->NamedObjectKey(str, *this->_pObject); +} + +void ImageJsonHandler::MimeTypeJsonHandler::reset(IJsonHandler* pParent, Image::MimeType* pEnum) { + JsonHandler::reset(pParent); + this->_pEnum = pEnum; +} + +IJsonHandler* ImageJsonHandler::MimeTypeJsonHandler::String(const char* str, size_t /*length*/, bool /*copy*/) { + using namespace std::string_literals; + + assert(this->_pEnum); + + if ("image/jpeg"s == str) *this->_pEnum = Image::MimeType::image_jpeg; + else if ("image/png"s == str) *this->_pEnum = Image::MimeType::image_png; + else return nullptr; + + return this->parent(); +} diff --git a/CesiumGltfReader/generated/ImageJsonHandler.h b/CesiumGltfReader/generated/ImageJsonHandler.h new file mode 100644 index 000000000..ece963690 --- /dev/null +++ b/CesiumGltfReader/generated/ImageJsonHandler.h @@ -0,0 +1,39 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/Image.h" +#include "IntegerJsonHandler.h" +#include "NamedObjectJsonHandler.h" +#include "StringJsonHandler.h" + +namespace CesiumGltf { + struct Image; + + class ImageJsonHandler : public NamedObjectJsonHandler { + public: + void reset(IJsonHandler* pHandler, Image* pObject); + Image* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* ImageKey(const char* str, Image& o); + + private: + class MimeTypeJsonHandler : public JsonHandler { + public: + void reset(IJsonHandler* pParent, Image::MimeType* pEnum); + virtual IJsonHandler* String(const char* str, size_t length, bool copy) override; + + private: + Image::MimeType* _pEnum = nullptr; + }; + + Image* _pObject; + StringJsonHandler _uri; + MimeTypeJsonHandler _mimeType; + IntegerJsonHandler _bufferView; + }; +} diff --git a/CesiumGltfReader/generated/KHR_draco_mesh_compressionJsonHandler.cpp b/CesiumGltfReader/generated/KHR_draco_mesh_compressionJsonHandler.cpp new file mode 100644 index 000000000..9a2104443 --- /dev/null +++ b/CesiumGltfReader/generated/KHR_draco_mesh_compressionJsonHandler.cpp @@ -0,0 +1,39 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#include "KHR_draco_mesh_compressionJsonHandler.h" +#include "CesiumGltf/KHR_draco_mesh_compression.h" + +#include +#include + +using namespace CesiumGltf; + +void KHR_draco_mesh_compressionJsonHandler::reset(IJsonHandler* pParent, KHR_draco_mesh_compression* pObject) { + ExtensibleObjectJsonHandler::reset(pParent, pObject); + this->_pObject = pObject; +} + +KHR_draco_mesh_compression* KHR_draco_mesh_compressionJsonHandler::getObject() { + return this->_pObject; +} + +void KHR_draco_mesh_compressionJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* KHR_draco_mesh_compressionJsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->KHR_draco_mesh_compressionKey(str, *this->_pObject); +} + +IJsonHandler* KHR_draco_mesh_compressionJsonHandler::KHR_draco_mesh_compressionKey(const char* str, KHR_draco_mesh_compression& o) { + using namespace std::string_literals; + + if ("bufferView"s == str) return property("bufferView", this->_bufferView, o.bufferView); + if ("attributes"s == str) return property("attributes", this->_attributes, o.attributes); + + return this->ExtensibleObjectKey(str, *this->_pObject); +} diff --git a/CesiumGltfReader/generated/KHR_draco_mesh_compressionJsonHandler.h b/CesiumGltfReader/generated/KHR_draco_mesh_compressionJsonHandler.h new file mode 100644 index 000000000..8312d7991 --- /dev/null +++ b/CesiumGltfReader/generated/KHR_draco_mesh_compressionJsonHandler.h @@ -0,0 +1,29 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "DictionaryJsonHandler.h" +#include "ExtensibleObjectJsonHandler.h" +#include "IntegerJsonHandler.h" + +namespace CesiumGltf { + struct KHR_draco_mesh_compression; + + class KHR_draco_mesh_compressionJsonHandler : public ExtensibleObjectJsonHandler { + public: + void reset(IJsonHandler* pHandler, KHR_draco_mesh_compression* pObject); + KHR_draco_mesh_compression* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* KHR_draco_mesh_compressionKey(const char* str, KHR_draco_mesh_compression& o); + + private: + + KHR_draco_mesh_compression* _pObject; + IntegerJsonHandler _bufferView; + DictionaryJsonHandler> _attributes; + }; +} diff --git a/CesiumGltfReader/generated/MaterialJsonHandler.cpp b/CesiumGltfReader/generated/MaterialJsonHandler.cpp new file mode 100644 index 000000000..1065e9097 --- /dev/null +++ b/CesiumGltfReader/generated/MaterialJsonHandler.cpp @@ -0,0 +1,63 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#include "MaterialJsonHandler.h" +#include "CesiumGltf/Material.h" + +#include +#include + +using namespace CesiumGltf; + +void MaterialJsonHandler::reset(IJsonHandler* pParent, Material* pObject) { + NamedObjectJsonHandler::reset(pParent, pObject); + this->_pObject = pObject; +} + +Material* MaterialJsonHandler::getObject() { + return this->_pObject; +} + +void MaterialJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* MaterialJsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->MaterialKey(str, *this->_pObject); +} + +IJsonHandler* MaterialJsonHandler::MaterialKey(const char* str, Material& o) { + using namespace std::string_literals; + + if ("pbrMetallicRoughness"s == str) return property("pbrMetallicRoughness", this->_pbrMetallicRoughness, o.pbrMetallicRoughness); + if ("normalTexture"s == str) return property("normalTexture", this->_normalTexture, o.normalTexture); + if ("occlusionTexture"s == str) return property("occlusionTexture", this->_occlusionTexture, o.occlusionTexture); + if ("emissiveTexture"s == str) return property("emissiveTexture", this->_emissiveTexture, o.emissiveTexture); + if ("emissiveFactor"s == str) return property("emissiveFactor", this->_emissiveFactor, o.emissiveFactor); + if ("alphaMode"s == str) return property("alphaMode", this->_alphaMode, o.alphaMode); + if ("alphaCutoff"s == str) return property("alphaCutoff", this->_alphaCutoff, o.alphaCutoff); + if ("doubleSided"s == str) return property("doubleSided", this->_doubleSided, o.doubleSided); + + return this->NamedObjectKey(str, *this->_pObject); +} + +void MaterialJsonHandler::AlphaModeJsonHandler::reset(IJsonHandler* pParent, Material::AlphaMode* pEnum) { + JsonHandler::reset(pParent); + this->_pEnum = pEnum; +} + +IJsonHandler* MaterialJsonHandler::AlphaModeJsonHandler::String(const char* str, size_t /*length*/, bool /*copy*/) { + using namespace std::string_literals; + + assert(this->_pEnum); + + if ("OPAQUE"s == str) *this->_pEnum = Material::AlphaMode::OPAQUE; + else if ("MASK"s == str) *this->_pEnum = Material::AlphaMode::MASK; + else if ("BLEND"s == str) *this->_pEnum = Material::AlphaMode::BLEND; + else return nullptr; + + return this->parent(); +} diff --git a/CesiumGltfReader/generated/MaterialJsonHandler.h b/CesiumGltfReader/generated/MaterialJsonHandler.h new file mode 100644 index 000000000..265d9ac48 --- /dev/null +++ b/CesiumGltfReader/generated/MaterialJsonHandler.h @@ -0,0 +1,49 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "ArrayJsonHandler.h" +#include "BoolJsonHandler.h" +#include "CesiumGltf/Material.h" +#include "DoubleJsonHandler.h" +#include "MaterialNormalTextureInfoJsonHandler.h" +#include "MaterialOcclusionTextureInfoJsonHandler.h" +#include "MaterialPBRMetallicRoughnessJsonHandler.h" +#include "NamedObjectJsonHandler.h" +#include "TextureInfoJsonHandler.h" + +namespace CesiumGltf { + struct Material; + + class MaterialJsonHandler : public NamedObjectJsonHandler { + public: + void reset(IJsonHandler* pHandler, Material* pObject); + Material* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* MaterialKey(const char* str, Material& o); + + private: + class AlphaModeJsonHandler : public JsonHandler { + public: + void reset(IJsonHandler* pParent, Material::AlphaMode* pEnum); + virtual IJsonHandler* String(const char* str, size_t length, bool copy) override; + + private: + Material::AlphaMode* _pEnum = nullptr; + }; + + Material* _pObject; + MaterialPBRMetallicRoughnessJsonHandler _pbrMetallicRoughness; + MaterialNormalTextureInfoJsonHandler _normalTexture; + MaterialOcclusionTextureInfoJsonHandler _occlusionTexture; + TextureInfoJsonHandler _emissiveTexture; + ArrayJsonHandler _emissiveFactor; + AlphaModeJsonHandler _alphaMode; + DoubleJsonHandler _alphaCutoff; + BoolJsonHandler _doubleSided; + }; +} diff --git a/CesiumGltfReader/generated/MaterialNormalTextureInfoJsonHandler.cpp b/CesiumGltfReader/generated/MaterialNormalTextureInfoJsonHandler.cpp new file mode 100644 index 000000000..ba1e5158d --- /dev/null +++ b/CesiumGltfReader/generated/MaterialNormalTextureInfoJsonHandler.cpp @@ -0,0 +1,38 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#include "MaterialNormalTextureInfoJsonHandler.h" +#include "CesiumGltf/MaterialNormalTextureInfo.h" + +#include +#include + +using namespace CesiumGltf; + +void MaterialNormalTextureInfoJsonHandler::reset(IJsonHandler* pParent, MaterialNormalTextureInfo* pObject) { + TextureInfoJsonHandler::reset(pParent, pObject); + this->_pObject = pObject; +} + +MaterialNormalTextureInfo* MaterialNormalTextureInfoJsonHandler::getObject() { + return this->_pObject; +} + +void MaterialNormalTextureInfoJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* MaterialNormalTextureInfoJsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->MaterialNormalTextureInfoKey(str, *this->_pObject); +} + +IJsonHandler* MaterialNormalTextureInfoJsonHandler::MaterialNormalTextureInfoKey(const char* str, MaterialNormalTextureInfo& o) { + using namespace std::string_literals; + + if ("scale"s == str) return property("scale", this->_scale, o.scale); + + return this->TextureInfoKey(str, *this->_pObject); +} diff --git a/CesiumGltfReader/generated/MaterialNormalTextureInfoJsonHandler.h b/CesiumGltfReader/generated/MaterialNormalTextureInfoJsonHandler.h new file mode 100644 index 000000000..a6ba3d3a6 --- /dev/null +++ b/CesiumGltfReader/generated/MaterialNormalTextureInfoJsonHandler.h @@ -0,0 +1,27 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "DoubleJsonHandler.h" +#include "TextureInfoJsonHandler.h" + +namespace CesiumGltf { + struct MaterialNormalTextureInfo; + + class MaterialNormalTextureInfoJsonHandler : public TextureInfoJsonHandler { + public: + void reset(IJsonHandler* pHandler, MaterialNormalTextureInfo* pObject); + MaterialNormalTextureInfo* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* MaterialNormalTextureInfoKey(const char* str, MaterialNormalTextureInfo& o); + + private: + + MaterialNormalTextureInfo* _pObject; + DoubleJsonHandler _scale; + }; +} diff --git a/CesiumGltfReader/generated/MaterialOcclusionTextureInfoJsonHandler.cpp b/CesiumGltfReader/generated/MaterialOcclusionTextureInfoJsonHandler.cpp new file mode 100644 index 000000000..160b19b8a --- /dev/null +++ b/CesiumGltfReader/generated/MaterialOcclusionTextureInfoJsonHandler.cpp @@ -0,0 +1,38 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#include "MaterialOcclusionTextureInfoJsonHandler.h" +#include "CesiumGltf/MaterialOcclusionTextureInfo.h" + +#include +#include + +using namespace CesiumGltf; + +void MaterialOcclusionTextureInfoJsonHandler::reset(IJsonHandler* pParent, MaterialOcclusionTextureInfo* pObject) { + TextureInfoJsonHandler::reset(pParent, pObject); + this->_pObject = pObject; +} + +MaterialOcclusionTextureInfo* MaterialOcclusionTextureInfoJsonHandler::getObject() { + return this->_pObject; +} + +void MaterialOcclusionTextureInfoJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* MaterialOcclusionTextureInfoJsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->MaterialOcclusionTextureInfoKey(str, *this->_pObject); +} + +IJsonHandler* MaterialOcclusionTextureInfoJsonHandler::MaterialOcclusionTextureInfoKey(const char* str, MaterialOcclusionTextureInfo& o) { + using namespace std::string_literals; + + if ("strength"s == str) return property("strength", this->_strength, o.strength); + + return this->TextureInfoKey(str, *this->_pObject); +} diff --git a/CesiumGltfReader/generated/MaterialOcclusionTextureInfoJsonHandler.h b/CesiumGltfReader/generated/MaterialOcclusionTextureInfoJsonHandler.h new file mode 100644 index 000000000..a0d812d6a --- /dev/null +++ b/CesiumGltfReader/generated/MaterialOcclusionTextureInfoJsonHandler.h @@ -0,0 +1,27 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "DoubleJsonHandler.h" +#include "TextureInfoJsonHandler.h" + +namespace CesiumGltf { + struct MaterialOcclusionTextureInfo; + + class MaterialOcclusionTextureInfoJsonHandler : public TextureInfoJsonHandler { + public: + void reset(IJsonHandler* pHandler, MaterialOcclusionTextureInfo* pObject); + MaterialOcclusionTextureInfo* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* MaterialOcclusionTextureInfoKey(const char* str, MaterialOcclusionTextureInfo& o); + + private: + + MaterialOcclusionTextureInfo* _pObject; + DoubleJsonHandler _strength; + }; +} diff --git a/CesiumGltfReader/generated/MaterialPBRMetallicRoughnessJsonHandler.cpp b/CesiumGltfReader/generated/MaterialPBRMetallicRoughnessJsonHandler.cpp new file mode 100644 index 000000000..faacfce39 --- /dev/null +++ b/CesiumGltfReader/generated/MaterialPBRMetallicRoughnessJsonHandler.cpp @@ -0,0 +1,42 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#include "MaterialPBRMetallicRoughnessJsonHandler.h" +#include "CesiumGltf/MaterialPBRMetallicRoughness.h" + +#include +#include + +using namespace CesiumGltf; + +void MaterialPBRMetallicRoughnessJsonHandler::reset(IJsonHandler* pParent, MaterialPBRMetallicRoughness* pObject) { + ExtensibleObjectJsonHandler::reset(pParent, pObject); + this->_pObject = pObject; +} + +MaterialPBRMetallicRoughness* MaterialPBRMetallicRoughnessJsonHandler::getObject() { + return this->_pObject; +} + +void MaterialPBRMetallicRoughnessJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* MaterialPBRMetallicRoughnessJsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->MaterialPBRMetallicRoughnessKey(str, *this->_pObject); +} + +IJsonHandler* MaterialPBRMetallicRoughnessJsonHandler::MaterialPBRMetallicRoughnessKey(const char* str, MaterialPBRMetallicRoughness& o) { + using namespace std::string_literals; + + if ("baseColorFactor"s == str) return property("baseColorFactor", this->_baseColorFactor, o.baseColorFactor); + if ("baseColorTexture"s == str) return property("baseColorTexture", this->_baseColorTexture, o.baseColorTexture); + if ("metallicFactor"s == str) return property("metallicFactor", this->_metallicFactor, o.metallicFactor); + if ("roughnessFactor"s == str) return property("roughnessFactor", this->_roughnessFactor, o.roughnessFactor); + if ("metallicRoughnessTexture"s == str) return property("metallicRoughnessTexture", this->_metallicRoughnessTexture, o.metallicRoughnessTexture); + + return this->ExtensibleObjectKey(str, *this->_pObject); +} diff --git a/CesiumGltfReader/generated/MaterialPBRMetallicRoughnessJsonHandler.h b/CesiumGltfReader/generated/MaterialPBRMetallicRoughnessJsonHandler.h new file mode 100644 index 000000000..39e8a8272 --- /dev/null +++ b/CesiumGltfReader/generated/MaterialPBRMetallicRoughnessJsonHandler.h @@ -0,0 +1,33 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "ArrayJsonHandler.h" +#include "DoubleJsonHandler.h" +#include "ExtensibleObjectJsonHandler.h" +#include "TextureInfoJsonHandler.h" + +namespace CesiumGltf { + struct MaterialPBRMetallicRoughness; + + class MaterialPBRMetallicRoughnessJsonHandler : public ExtensibleObjectJsonHandler { + public: + void reset(IJsonHandler* pHandler, MaterialPBRMetallicRoughness* pObject); + MaterialPBRMetallicRoughness* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* MaterialPBRMetallicRoughnessKey(const char* str, MaterialPBRMetallicRoughness& o); + + private: + + MaterialPBRMetallicRoughness* _pObject; + ArrayJsonHandler _baseColorFactor; + TextureInfoJsonHandler _baseColorTexture; + DoubleJsonHandler _metallicFactor; + DoubleJsonHandler _roughnessFactor; + TextureInfoJsonHandler _metallicRoughnessTexture; + }; +} diff --git a/CesiumGltfReader/generated/MeshJsonHandler.cpp b/CesiumGltfReader/generated/MeshJsonHandler.cpp new file mode 100644 index 000000000..9988f139d --- /dev/null +++ b/CesiumGltfReader/generated/MeshJsonHandler.cpp @@ -0,0 +1,39 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#include "MeshJsonHandler.h" +#include "CesiumGltf/Mesh.h" + +#include +#include + +using namespace CesiumGltf; + +void MeshJsonHandler::reset(IJsonHandler* pParent, Mesh* pObject) { + NamedObjectJsonHandler::reset(pParent, pObject); + this->_pObject = pObject; +} + +Mesh* MeshJsonHandler::getObject() { + return this->_pObject; +} + +void MeshJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* MeshJsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->MeshKey(str, *this->_pObject); +} + +IJsonHandler* MeshJsonHandler::MeshKey(const char* str, Mesh& o) { + using namespace std::string_literals; + + if ("primitives"s == str) return property("primitives", this->_primitives, o.primitives); + if ("weights"s == str) return property("weights", this->_weights, o.weights); + + return this->NamedObjectKey(str, *this->_pObject); +} diff --git a/CesiumGltfReader/generated/MeshJsonHandler.h b/CesiumGltfReader/generated/MeshJsonHandler.h new file mode 100644 index 000000000..cb9e7814c --- /dev/null +++ b/CesiumGltfReader/generated/MeshJsonHandler.h @@ -0,0 +1,30 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "ArrayJsonHandler.h" +#include "DoubleJsonHandler.h" +#include "MeshPrimitiveJsonHandler.h" +#include "NamedObjectJsonHandler.h" + +namespace CesiumGltf { + struct Mesh; + + class MeshJsonHandler : public NamedObjectJsonHandler { + public: + void reset(IJsonHandler* pHandler, Mesh* pObject); + Mesh* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* MeshKey(const char* str, Mesh& o); + + private: + + Mesh* _pObject; + ArrayJsonHandler _primitives; + ArrayJsonHandler _weights; + }; +} diff --git a/CesiumGltfReader/generated/MeshPrimitiveJsonHandler.cpp b/CesiumGltfReader/generated/MeshPrimitiveJsonHandler.cpp new file mode 100644 index 000000000..311be506e --- /dev/null +++ b/CesiumGltfReader/generated/MeshPrimitiveJsonHandler.cpp @@ -0,0 +1,56 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#include "MeshPrimitiveJsonHandler.h" +#include "CesiumGltf/MeshPrimitive.h" +#include "CesiumGltf/KHR_draco_mesh_compression.h" +#include +#include + +using namespace CesiumGltf; + +void MeshPrimitiveJsonHandler::reset(IJsonHandler* pParent, MeshPrimitive* pObject) { + ExtensibleObjectJsonHandler::reset(pParent, pObject); + this->_pObject = pObject; +} + +MeshPrimitive* MeshPrimitiveJsonHandler::getObject() { + return this->_pObject; +} + +void MeshPrimitiveJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* MeshPrimitiveJsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->MeshPrimitiveKey(str, *this->_pObject); +} + +IJsonHandler* MeshPrimitiveJsonHandler::MeshPrimitiveKey(const char* str, MeshPrimitive& o) { + using namespace std::string_literals; + + if ("attributes"s == str) return property("attributes", this->_attributes, o.attributes); + if ("indices"s == str) return property("indices", this->_indices, o.indices); + if ("material"s == str) return property("material", this->_material, o.material); + if ("mode"s == str) return property("mode", this->_mode, o.mode); + if ("targets"s == str) return property("targets", this->_targets, o.targets); + if ("extensions"s == str) return property("extensions", this->_extensions, o.extensions); + + return this->ExtensibleObjectKey(str, *this->_pObject); +} + +void MeshPrimitiveJsonHandler::ExtensionsJsonHandler::reset(IJsonHandler* pParent, std::vector* pExtensions) { + ObjectJsonHandler::reset(pParent); + this->_pExtensions = pExtensions; +} + +IJsonHandler* MeshPrimitiveJsonHandler::ExtensionsJsonHandler::Key(const char* str, size_t /* length */, bool /* copy */) { + using namespace std::string_literals; + + if ("KHR_draco_mesh_compression"s == str) return property("KHR_draco_mesh_compression", this->_KHR_draco_mesh_compression, std::any_cast(this->_pExtensions->emplace_back(KHR_draco_mesh_compression()))); + + return this->ignoreAndContinue(); +} diff --git a/CesiumGltfReader/generated/MeshPrimitiveJsonHandler.h b/CesiumGltfReader/generated/MeshPrimitiveJsonHandler.h new file mode 100644 index 000000000..5bd507fcd --- /dev/null +++ b/CesiumGltfReader/generated/MeshPrimitiveJsonHandler.h @@ -0,0 +1,45 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "ArrayJsonHandler.h" +#include "CesiumGltf/MeshPrimitive.h" +#include "DictionaryJsonHandler.h" +#include "ExtensibleObjectJsonHandler.h" +#include "IntegerJsonHandler.h" +#include "KHR_draco_mesh_compressionJsonHandler.h" + +namespace CesiumGltf { + struct MeshPrimitive; + + class MeshPrimitiveJsonHandler : public ExtensibleObjectJsonHandler { + public: + void reset(IJsonHandler* pHandler, MeshPrimitive* pObject); + MeshPrimitive* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* MeshPrimitiveKey(const char* str, MeshPrimitive& o); + + private: + class ExtensionsJsonHandler : public ObjectJsonHandler { + public: + void reset(IJsonHandler* pParent, std::vector* pExtensions); + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + private: + std::vector* _pExtensions = nullptr; + KHR_draco_mesh_compressionJsonHandler _KHR_draco_mesh_compression; + }; + + MeshPrimitive* _pObject; + DictionaryJsonHandler> _attributes; + IntegerJsonHandler _indices; + IntegerJsonHandler _material; + IntegerJsonHandler _mode; + ArrayJsonHandler, DictionaryJsonHandler>> _targets; + ExtensionsJsonHandler _extensions; + }; +} diff --git a/CesiumGltfReader/generated/ModelJsonHandler.cpp b/CesiumGltfReader/generated/ModelJsonHandler.cpp new file mode 100644 index 000000000..5265cf3b0 --- /dev/null +++ b/CesiumGltfReader/generated/ModelJsonHandler.cpp @@ -0,0 +1,54 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#include "ModelJsonHandler.h" +#include "CesiumGltf/Model.h" + +#include +#include + +using namespace CesiumGltf; + +void ModelJsonHandler::reset(IJsonHandler* pParent, Model* pObject) { + ExtensibleObjectJsonHandler::reset(pParent, pObject); + this->_pObject = pObject; +} + +Model* ModelJsonHandler::getObject() { + return this->_pObject; +} + +void ModelJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* ModelJsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->ModelKey(str, *this->_pObject); +} + +IJsonHandler* ModelJsonHandler::ModelKey(const char* str, Model& o) { + using namespace std::string_literals; + + if ("extensionsUsed"s == str) return property("extensionsUsed", this->_extensionsUsed, o.extensionsUsed); + if ("extensionsRequired"s == str) return property("extensionsRequired", this->_extensionsRequired, o.extensionsRequired); + if ("accessors"s == str) return property("accessors", this->_accessors, o.accessors); + if ("animations"s == str) return property("animations", this->_animations, o.animations); + if ("asset"s == str) return property("asset", this->_asset, o.asset); + if ("buffers"s == str) return property("buffers", this->_buffers, o.buffers); + if ("bufferViews"s == str) return property("bufferViews", this->_bufferViews, o.bufferViews); + if ("cameras"s == str) return property("cameras", this->_cameras, o.cameras); + if ("images"s == str) return property("images", this->_images, o.images); + if ("materials"s == str) return property("materials", this->_materials, o.materials); + if ("meshes"s == str) return property("meshes", this->_meshes, o.meshes); + if ("nodes"s == str) return property("nodes", this->_nodes, o.nodes); + if ("samplers"s == str) return property("samplers", this->_samplers, o.samplers); + if ("scene"s == str) return property("scene", this->_scene, o.scene); + if ("scenes"s == str) return property("scenes", this->_scenes, o.scenes); + if ("skins"s == str) return property("skins", this->_skins, o.skins); + if ("textures"s == str) return property("textures", this->_textures, o.textures); + + return this->ExtensibleObjectKey(str, *this->_pObject); +} diff --git a/CesiumGltfReader/generated/ModelJsonHandler.h b/CesiumGltfReader/generated/ModelJsonHandler.h new file mode 100644 index 000000000..a885d732b --- /dev/null +++ b/CesiumGltfReader/generated/ModelJsonHandler.h @@ -0,0 +1,59 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "AccessorJsonHandler.h" +#include "AnimationJsonHandler.h" +#include "ArrayJsonHandler.h" +#include "AssetJsonHandler.h" +#include "BufferJsonHandler.h" +#include "BufferViewJsonHandler.h" +#include "CameraJsonHandler.h" +#include "ExtensibleObjectJsonHandler.h" +#include "ImageJsonHandler.h" +#include "IntegerJsonHandler.h" +#include "MaterialJsonHandler.h" +#include "MeshJsonHandler.h" +#include "NodeJsonHandler.h" +#include "SamplerJsonHandler.h" +#include "SceneJsonHandler.h" +#include "SkinJsonHandler.h" +#include "StringJsonHandler.h" +#include "TextureJsonHandler.h" + +namespace CesiumGltf { + struct Model; + + class ModelJsonHandler : public ExtensibleObjectJsonHandler { + public: + void reset(IJsonHandler* pHandler, Model* pObject); + Model* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* ModelKey(const char* str, Model& o); + + private: + + Model* _pObject; + ArrayJsonHandler _extensionsUsed; + ArrayJsonHandler _extensionsRequired; + ArrayJsonHandler _accessors; + ArrayJsonHandler _animations; + AssetJsonHandler _asset; + ArrayJsonHandler _buffers; + ArrayJsonHandler _bufferViews; + ArrayJsonHandler _cameras; + ArrayJsonHandler _images; + ArrayJsonHandler _materials; + ArrayJsonHandler _meshes; + ArrayJsonHandler _nodes; + ArrayJsonHandler _samplers; + IntegerJsonHandler _scene; + ArrayJsonHandler _scenes; + ArrayJsonHandler _skins; + ArrayJsonHandler _textures; + }; +} diff --git a/CesiumGltfReader/generated/NodeJsonHandler.cpp b/CesiumGltfReader/generated/NodeJsonHandler.cpp new file mode 100644 index 000000000..a34eb4188 --- /dev/null +++ b/CesiumGltfReader/generated/NodeJsonHandler.cpp @@ -0,0 +1,46 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#include "NodeJsonHandler.h" +#include "CesiumGltf/Node.h" + +#include +#include + +using namespace CesiumGltf; + +void NodeJsonHandler::reset(IJsonHandler* pParent, Node* pObject) { + NamedObjectJsonHandler::reset(pParent, pObject); + this->_pObject = pObject; +} + +Node* NodeJsonHandler::getObject() { + return this->_pObject; +} + +void NodeJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* NodeJsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->NodeKey(str, *this->_pObject); +} + +IJsonHandler* NodeJsonHandler::NodeKey(const char* str, Node& o) { + using namespace std::string_literals; + + if ("camera"s == str) return property("camera", this->_camera, o.camera); + if ("children"s == str) return property("children", this->_children, o.children); + if ("skin"s == str) return property("skin", this->_skin, o.skin); + if ("matrix"s == str) return property("matrix", this->_matrix, o.matrix); + if ("mesh"s == str) return property("mesh", this->_mesh, o.mesh); + if ("rotation"s == str) return property("rotation", this->_rotation, o.rotation); + if ("scale"s == str) return property("scale", this->_scale, o.scale); + if ("translation"s == str) return property("translation", this->_translation, o.translation); + if ("weights"s == str) return property("weights", this->_weights, o.weights); + + return this->NamedObjectKey(str, *this->_pObject); +} diff --git a/CesiumGltfReader/generated/NodeJsonHandler.h b/CesiumGltfReader/generated/NodeJsonHandler.h new file mode 100644 index 000000000..1515b89e9 --- /dev/null +++ b/CesiumGltfReader/generated/NodeJsonHandler.h @@ -0,0 +1,37 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "ArrayJsonHandler.h" +#include "DoubleJsonHandler.h" +#include "IntegerJsonHandler.h" +#include "NamedObjectJsonHandler.h" + +namespace CesiumGltf { + struct Node; + + class NodeJsonHandler : public NamedObjectJsonHandler { + public: + void reset(IJsonHandler* pHandler, Node* pObject); + Node* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* NodeKey(const char* str, Node& o); + + private: + + Node* _pObject; + IntegerJsonHandler _camera; + ArrayJsonHandler> _children; + IntegerJsonHandler _skin; + ArrayJsonHandler _matrix; + IntegerJsonHandler _mesh; + ArrayJsonHandler _rotation; + ArrayJsonHandler _scale; + ArrayJsonHandler _translation; + ArrayJsonHandler _weights; + }; +} diff --git a/CesiumGltfReader/generated/SamplerJsonHandler.cpp b/CesiumGltfReader/generated/SamplerJsonHandler.cpp new file mode 100644 index 000000000..4e52069f0 --- /dev/null +++ b/CesiumGltfReader/generated/SamplerJsonHandler.cpp @@ -0,0 +1,41 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#include "SamplerJsonHandler.h" +#include "CesiumGltf/Sampler.h" + +#include +#include + +using namespace CesiumGltf; + +void SamplerJsonHandler::reset(IJsonHandler* pParent, Sampler* pObject) { + NamedObjectJsonHandler::reset(pParent, pObject); + this->_pObject = pObject; +} + +Sampler* SamplerJsonHandler::getObject() { + return this->_pObject; +} + +void SamplerJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* SamplerJsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->SamplerKey(str, *this->_pObject); +} + +IJsonHandler* SamplerJsonHandler::SamplerKey(const char* str, Sampler& o) { + using namespace std::string_literals; + + if ("magFilter"s == str) return property("magFilter", this->_magFilter, o.magFilter); + if ("minFilter"s == str) return property("minFilter", this->_minFilter, o.minFilter); + if ("wrapS"s == str) return property("wrapS", this->_wrapS, o.wrapS); + if ("wrapT"s == str) return property("wrapT", this->_wrapT, o.wrapT); + + return this->NamedObjectKey(str, *this->_pObject); +} diff --git a/CesiumGltfReader/generated/SamplerJsonHandler.h b/CesiumGltfReader/generated/SamplerJsonHandler.h new file mode 100644 index 000000000..db2981232 --- /dev/null +++ b/CesiumGltfReader/generated/SamplerJsonHandler.h @@ -0,0 +1,31 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "CesiumGltf/Sampler.h" +#include "IntegerJsonHandler.h" +#include "NamedObjectJsonHandler.h" + +namespace CesiumGltf { + struct Sampler; + + class SamplerJsonHandler : public NamedObjectJsonHandler { + public: + void reset(IJsonHandler* pHandler, Sampler* pObject); + Sampler* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* SamplerKey(const char* str, Sampler& o); + + private: + + Sampler* _pObject; + IntegerJsonHandler _magFilter; + IntegerJsonHandler _minFilter; + IntegerJsonHandler _wrapS; + IntegerJsonHandler _wrapT; + }; +} diff --git a/CesiumGltfReader/generated/SceneJsonHandler.cpp b/CesiumGltfReader/generated/SceneJsonHandler.cpp new file mode 100644 index 000000000..01c9f5248 --- /dev/null +++ b/CesiumGltfReader/generated/SceneJsonHandler.cpp @@ -0,0 +1,38 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#include "SceneJsonHandler.h" +#include "CesiumGltf/Scene.h" + +#include +#include + +using namespace CesiumGltf; + +void SceneJsonHandler::reset(IJsonHandler* pParent, Scene* pObject) { + NamedObjectJsonHandler::reset(pParent, pObject); + this->_pObject = pObject; +} + +Scene* SceneJsonHandler::getObject() { + return this->_pObject; +} + +void SceneJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* SceneJsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->SceneKey(str, *this->_pObject); +} + +IJsonHandler* SceneJsonHandler::SceneKey(const char* str, Scene& o) { + using namespace std::string_literals; + + if ("nodes"s == str) return property("nodes", this->_nodes, o.nodes); + + return this->NamedObjectKey(str, *this->_pObject); +} diff --git a/CesiumGltfReader/generated/SceneJsonHandler.h b/CesiumGltfReader/generated/SceneJsonHandler.h new file mode 100644 index 000000000..8c603b808 --- /dev/null +++ b/CesiumGltfReader/generated/SceneJsonHandler.h @@ -0,0 +1,28 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "ArrayJsonHandler.h" +#include "IntegerJsonHandler.h" +#include "NamedObjectJsonHandler.h" + +namespace CesiumGltf { + struct Scene; + + class SceneJsonHandler : public NamedObjectJsonHandler { + public: + void reset(IJsonHandler* pHandler, Scene* pObject); + Scene* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* SceneKey(const char* str, Scene& o); + + private: + + Scene* _pObject; + ArrayJsonHandler> _nodes; + }; +} diff --git a/CesiumGltfReader/generated/SkinJsonHandler.cpp b/CesiumGltfReader/generated/SkinJsonHandler.cpp new file mode 100644 index 000000000..fb8aefeb9 --- /dev/null +++ b/CesiumGltfReader/generated/SkinJsonHandler.cpp @@ -0,0 +1,40 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#include "SkinJsonHandler.h" +#include "CesiumGltf/Skin.h" + +#include +#include + +using namespace CesiumGltf; + +void SkinJsonHandler::reset(IJsonHandler* pParent, Skin* pObject) { + NamedObjectJsonHandler::reset(pParent, pObject); + this->_pObject = pObject; +} + +Skin* SkinJsonHandler::getObject() { + return this->_pObject; +} + +void SkinJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* SkinJsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->SkinKey(str, *this->_pObject); +} + +IJsonHandler* SkinJsonHandler::SkinKey(const char* str, Skin& o) { + using namespace std::string_literals; + + if ("inverseBindMatrices"s == str) return property("inverseBindMatrices", this->_inverseBindMatrices, o.inverseBindMatrices); + if ("skeleton"s == str) return property("skeleton", this->_skeleton, o.skeleton); + if ("joints"s == str) return property("joints", this->_joints, o.joints); + + return this->NamedObjectKey(str, *this->_pObject); +} diff --git a/CesiumGltfReader/generated/SkinJsonHandler.h b/CesiumGltfReader/generated/SkinJsonHandler.h new file mode 100644 index 000000000..89db00122 --- /dev/null +++ b/CesiumGltfReader/generated/SkinJsonHandler.h @@ -0,0 +1,30 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "ArrayJsonHandler.h" +#include "IntegerJsonHandler.h" +#include "NamedObjectJsonHandler.h" + +namespace CesiumGltf { + struct Skin; + + class SkinJsonHandler : public NamedObjectJsonHandler { + public: + void reset(IJsonHandler* pHandler, Skin* pObject); + Skin* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* SkinKey(const char* str, Skin& o); + + private: + + Skin* _pObject; + IntegerJsonHandler _inverseBindMatrices; + IntegerJsonHandler _skeleton; + ArrayJsonHandler> _joints; + }; +} diff --git a/CesiumGltfReader/generated/TextureInfoJsonHandler.cpp b/CesiumGltfReader/generated/TextureInfoJsonHandler.cpp new file mode 100644 index 000000000..96b325d4b --- /dev/null +++ b/CesiumGltfReader/generated/TextureInfoJsonHandler.cpp @@ -0,0 +1,39 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#include "TextureInfoJsonHandler.h" +#include "CesiumGltf/TextureInfo.h" + +#include +#include + +using namespace CesiumGltf; + +void TextureInfoJsonHandler::reset(IJsonHandler* pParent, TextureInfo* pObject) { + ExtensibleObjectJsonHandler::reset(pParent, pObject); + this->_pObject = pObject; +} + +TextureInfo* TextureInfoJsonHandler::getObject() { + return this->_pObject; +} + +void TextureInfoJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* TextureInfoJsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->TextureInfoKey(str, *this->_pObject); +} + +IJsonHandler* TextureInfoJsonHandler::TextureInfoKey(const char* str, TextureInfo& o) { + using namespace std::string_literals; + + if ("index"s == str) return property("index", this->_index, o.index); + if ("texCoord"s == str) return property("texCoord", this->_texCoord, o.texCoord); + + return this->ExtensibleObjectKey(str, *this->_pObject); +} diff --git a/CesiumGltfReader/generated/TextureInfoJsonHandler.h b/CesiumGltfReader/generated/TextureInfoJsonHandler.h new file mode 100644 index 000000000..c09a6861a --- /dev/null +++ b/CesiumGltfReader/generated/TextureInfoJsonHandler.h @@ -0,0 +1,28 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "ExtensibleObjectJsonHandler.h" +#include "IntegerJsonHandler.h" + +namespace CesiumGltf { + struct TextureInfo; + + class TextureInfoJsonHandler : public ExtensibleObjectJsonHandler { + public: + void reset(IJsonHandler* pHandler, TextureInfo* pObject); + TextureInfo* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* TextureInfoKey(const char* str, TextureInfo& o); + + private: + + TextureInfo* _pObject; + IntegerJsonHandler _index; + IntegerJsonHandler _texCoord; + }; +} diff --git a/CesiumGltfReader/generated/TextureJsonHandler.cpp b/CesiumGltfReader/generated/TextureJsonHandler.cpp new file mode 100644 index 000000000..308e576aa --- /dev/null +++ b/CesiumGltfReader/generated/TextureJsonHandler.cpp @@ -0,0 +1,39 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#include "TextureJsonHandler.h" +#include "CesiumGltf/Texture.h" + +#include +#include + +using namespace CesiumGltf; + +void TextureJsonHandler::reset(IJsonHandler* pParent, Texture* pObject) { + NamedObjectJsonHandler::reset(pParent, pObject); + this->_pObject = pObject; +} + +Texture* TextureJsonHandler::getObject() { + return this->_pObject; +} + +void TextureJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* TextureJsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->TextureKey(str, *this->_pObject); +} + +IJsonHandler* TextureJsonHandler::TextureKey(const char* str, Texture& o) { + using namespace std::string_literals; + + if ("sampler"s == str) return property("sampler", this->_sampler, o.sampler); + if ("source"s == str) return property("source", this->_source, o.source); + + return this->NamedObjectKey(str, *this->_pObject); +} diff --git a/CesiumGltfReader/generated/TextureJsonHandler.h b/CesiumGltfReader/generated/TextureJsonHandler.h new file mode 100644 index 000000000..248796942 --- /dev/null +++ b/CesiumGltfReader/generated/TextureJsonHandler.h @@ -0,0 +1,28 @@ +// This file was generated by generate-gltf-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "IntegerJsonHandler.h" +#include "NamedObjectJsonHandler.h" + +namespace CesiumGltf { + struct Texture; + + class TextureJsonHandler : public NamedObjectJsonHandler { + public: + void reset(IJsonHandler* pHandler, Texture* pObject); + Texture* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* TextureKey(const char* str, Texture& o); + + private: + + Texture* _pObject; + IntegerJsonHandler _sampler; + IntegerJsonHandler _source; + }; +} diff --git a/CesiumGltfReader/include/CesiumGltf/Reader.h b/CesiumGltfReader/include/CesiumGltf/Reader.h new file mode 100644 index 000000000..3a73df50c --- /dev/null +++ b/CesiumGltfReader/include/CesiumGltf/Reader.h @@ -0,0 +1,90 @@ +#pragma once + +#include "CesiumGltf/ReaderLibrary.h" +#include "CesiumGltf/Model.h" +#include +#include +#include +#include + +namespace CesiumGltf { + /** + * @brief The result of reading a glTF model with {@link readModel}. + */ + struct CESIUMGLTFREADER_API ModelReaderResult { + /** + * @brief The read model, or std::nullopt if the model could not be read. + */ + std::optional model; + + /** + * @brief Errors, if any, that occurred during the load process. + */ + std::vector errors; + + /** + * @brief Warnings, if any, that occurred during the load process. + */ + std::vector warnings; + }; + + /** + * @brief Options for how to read a glTF. + */ + struct CESIUMGLTFREADER_API ReadModelOptions { + /** + * @brief Whether data URLs in buffers and images should be automatically decoded as part of the load process. + */ + bool decodeDataUrls = true; + + /** + * @brief Whether data URLs should be cleared after they are successfully decoded. + * + * This reduces the memory usage of the model. + */ + bool clearDecodedDataUrls = true; + + /** + * @brief Whether embedded images in {@link Model:buffers} should be automatically decoded as part of the load process. + * + * The {@link ImageSpec::mimeType} property is ignored, and instead the [stb_image](https://github.com/nothings/stb) library + * is used to decode images in `JPG`, `PNG`, `TGA`, `BMP`, `PSD`, `GIF`, `HDR`, or `PIC` format. + */ + bool decodeEmbeddedImages = true; + + /** + * @brief Whether geometry compressed using the `KHR_draco_mesh_compression` extension should be automatically decoded + * as part of the load process. + */ + bool decodeDraco = true; + }; + + /** + * @brief Reads a glTF or binary glTF (GLB) from a buffer. + * + * @param data The buffer from which to read the glTF. + * @param options Options for how to read the glTF. + * @return The result of reading the glTF. + */ + CESIUMGLTFREADER_API ModelReaderResult readModel(const gsl::span& data, const ReadModelOptions& options = ReadModelOptions()); + + /** + * @brief The result of reading an image with {@link readImage}. + */ + struct CESIUMGLTFREADER_API ImageReaderResult { + std::optional image; + std::vector errors; + std::vector warnings; + }; + + /** + * @brief Reads an image from a buffer. + * + * The [stb_image](https://github.com/nothings/stb) library is used to decode images in `JPG`, `PNG`, `TGA`, + * `BMP`, `PSD`, `GIF`, `HDR`, or `PIC` format. + * + * @param data The buffer from which to read the image. + * @return The result of reading the image. + */ + CESIUMGLTFREADER_API ImageReaderResult readImage(const gsl::span& data); +} diff --git a/CesiumGltfReader/include/CesiumGltf/ReaderLibrary.h b/CesiumGltfReader/include/CesiumGltf/ReaderLibrary.h new file mode 100644 index 000000000..4bd5ae889 --- /dev/null +++ b/CesiumGltfReader/include/CesiumGltf/ReaderLibrary.h @@ -0,0 +1,11 @@ +#pragma once + +#if defined(_WIN32) && defined(CESIUM_SHARED) + #ifdef CESIUMGLTFREADER_BUILDING + #define CESIUMGLTFREADER_API __declspec(dllexport) + #else + #define CESIUMGLTFREADER_API __declspec(dllimport) + #endif +#else + #define CESIUMGLTFREADER_API +#endif diff --git a/CesiumGltfReader/src/ArrayJsonHandler.h b/CesiumGltfReader/src/ArrayJsonHandler.h new file mode 100644 index 000000000..788df0b50 --- /dev/null +++ b/CesiumGltfReader/src/ArrayJsonHandler.h @@ -0,0 +1,406 @@ +#pragma once + +#include "JsonHandler.h" +#include "DoubleJsonHandler.h" +#include "IntegerJsonHandler.h" +#include "StringJsonHandler.h" +#include +#include + +namespace CesiumGltf { + template + class ArrayJsonHandler : public JsonHandler { + public: + void reset(IJsonHandler* pParent, std::vector* pArray) { + JsonHandler::reset(pParent); + this->_pArray = pArray; + this->_arrayIsOpen = false; + } + + virtual IJsonHandler* Null() override { + return this->invalid("A null")->Null(); + } + + virtual IJsonHandler* Bool(bool b) override { + return this->invalid("A boolean")->Bool(b); + } + + virtual IJsonHandler* Int(int i) override { + return this->invalid("An integer")->Int(i); + } + + virtual IJsonHandler* Uint(unsigned i) override { + return this->invalid("An integer")->Uint(i); + } + + virtual IJsonHandler* Int64(int64_t i) override { + return this->invalid("An integer")->Int64(i); + } + + virtual IJsonHandler* Uint64(uint64_t i) override { + return this->invalid("An integer")->Uint64(i); + } + + virtual IJsonHandler* Double(double d) override { + return this->invalid("A double (floating-point)")->Double(d); + } + + virtual IJsonHandler* String(const char* str, size_t length, bool copy) override { + return this->invalid("A string")->String(str, length, copy); + } + + virtual IJsonHandler* StartObject() override { + if (!this->_arrayIsOpen) { + return this->invalid("An object")->StartObject(); + } + + assert(this->_pArray); + T& o = this->_pArray->emplace_back(); + this->_objectHandler.reset(this, &o); + return this->_objectHandler.StartObject(); + } + + virtual IJsonHandler* Key(const char* /*str*/, size_t /*length*/, bool /*copy*/) override { + return nullptr; + } + + virtual IJsonHandler* EndObject(size_t /*memberCount*/) override { + return nullptr; + } + + virtual IJsonHandler* StartArray() override { + if (this->_arrayIsOpen) { + return this->invalid("An array")->StartArray(); + } + + this->_arrayIsOpen = true; + this->_pArray->clear(); + return this; + } + + virtual IJsonHandler* EndArray(size_t) override { + return this->parent(); + } + + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override { + context.push_back(std::string("[") + std::to_string(this->_pArray->size()) + "]"); + this->parent()->reportWarning(warning, std::move(context)); + } + + private: + IJsonHandler* invalid(const std::string& type) { + if (this->_arrayIsOpen) { + this->reportWarning(type + " value is not allowed in the object array and has been replaced with a default value."); + this->_pArray->emplace_back(); + return this->ignoreAndContinue(); + } else { + this->reportWarning(type + " is not allowed and has been ignored."); + return this->ignoreAndReturnToParent(); + } + } + + std::vector* _pArray = nullptr; + bool _arrayIsOpen = false; + THandler _objectHandler; + }; + + template <> + class ArrayJsonHandler : public JsonHandler { + public: + void reset(IJsonHandler* pParent, std::vector* pArray) { + JsonHandler::reset(pParent); + this->_pArray = pArray; + this->_arrayIsOpen = false; + } + + virtual IJsonHandler* Null() override { + return this->invalid("A null")->Null(); + } + + virtual IJsonHandler* Bool(bool b) override { + return this->invalid("A bool")->Bool(b); + } + + virtual IJsonHandler* Int(int i) override { + if (!this->_arrayIsOpen) { + return this->invalid("An integer")->Int(i); + } + + assert(this->_pArray); + this->_pArray->emplace_back(static_cast(i)); + return this; + } + + virtual IJsonHandler* Uint(unsigned i) override { + if (!this->_arrayIsOpen) { + return this->invalid("An integer")->Uint(i); + } + + assert(this->_pArray); + this->_pArray->emplace_back(static_cast(i)); + return this; + } + + virtual IJsonHandler* Int64(int64_t i) override { + if (!this->_arrayIsOpen) { + return this->invalid("An integer")->Int64(i); + } + + assert(this->_pArray); + this->_pArray->emplace_back(static_cast(i)); + return this; + } + + virtual IJsonHandler* Uint64(uint64_t i) override { + if (!this->_arrayIsOpen) { + return this->invalid("An integer")->Uint64(i); + } + + assert(this->_pArray); + this->_pArray->emplace_back(static_cast(i)); + return this; + } + + virtual IJsonHandler* Double(double d) override { + if (!this->_arrayIsOpen) { + return this->invalid("An integer")->Double(d); + } + + assert(this->_pArray); + this->_pArray->emplace_back(d); + return this; + } + + virtual IJsonHandler* String(const char* str, size_t length, bool copy) override { + return this->invalid("A string")->String(str, length, copy); + } + + virtual IJsonHandler* StartObject() override { + return this->invalid("An object")->StartObject(); + } + + virtual IJsonHandler* StartArray() override { + if (this->_arrayIsOpen) { + return this->invalid("An array")->StartArray(); + } + + this->_arrayIsOpen = true; + this->_pArray->clear(); + return this; + } + + virtual IJsonHandler* EndArray(size_t) override { + return this->parent(); + } + + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override { + context.push_back(std::string("[") + std::to_string(this->_pArray->size()) + "]"); + this->parent()->reportWarning(warning, std::move(context)); + } + + private: + IJsonHandler* invalid(const std::string& type) { + if (this->_arrayIsOpen) { + this->reportWarning(type + " value is not allowed in the double array and has been replaced with a default value."); + this->_pArray->emplace_back(); + return this->ignoreAndContinue(); + } else { + this->reportWarning(type + " is not allowed and has been ignored."); + return this->ignoreAndReturnToParent(); + } + } + + std::vector* _pArray = nullptr; + bool _arrayIsOpen = false; + }; + + template + class ArrayJsonHandler> : public JsonHandler { + public: + void reset(IJsonHandler* pParent, std::vector* pArray) { + JsonHandler::reset(pParent); + this->_pArray = pArray; + this->_arrayIsOpen = false; + } + + virtual IJsonHandler* Null() override { + return this->invalid("A null")->Null(); + } + + virtual IJsonHandler* Bool(bool b) override { + return this->invalid("A null")->Bool(b); + } + + virtual IJsonHandler* Int(int i) override { + if (!this->_arrayIsOpen) { + return this->invalid("An integer")->Int(i); + } + + assert(this->_pArray); + this->_pArray->emplace_back(static_cast(i)); + return this; + } + + virtual IJsonHandler* Uint(unsigned i) override { + if (!this->_arrayIsOpen) { + return this->invalid("An integer")->Uint(i); + } + + assert(this->_pArray); + this->_pArray->emplace_back(static_cast(i)); + return this; + } + + virtual IJsonHandler* Int64(int64_t i) override { + if (!this->_arrayIsOpen) { + return this->invalid("An integer")->Int64(i); + } + + assert(this->_pArray); + this->_pArray->emplace_back(static_cast(i)); + return this; + } + + virtual IJsonHandler* Uint64(uint64_t i) override { + if (!this->_arrayIsOpen) { + return this->invalid("An integer")->Uint64(i); + } + + assert(this->_pArray); + this->_pArray->emplace_back(static_cast(i)); + return this; + } + + virtual IJsonHandler* Double(double d) override { + return this->invalid("A double (floating-point)")->Double(d); + } + + virtual IJsonHandler* String(const char* str, size_t length, bool copy) override { + return this->invalid("A string")->String(str, length, copy); + } + + virtual IJsonHandler* StartObject() override { + return this->invalid("An object")->StartObject(); + } + + virtual IJsonHandler* StartArray() override { + if (this->_arrayIsOpen) { + return this->invalid("An array")->StartArray(); + } + + this->_arrayIsOpen = true; + this->_pArray->clear(); + return this; + } + + virtual IJsonHandler* EndArray(size_t) override { + return this->parent(); + } + + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override { + context.push_back(std::string("[") + std::to_string(this->_pArray->size()) + "]"); + this->parent()->reportWarning(warning, std::move(context)); + } + + private: + IJsonHandler* invalid(const std::string& type) { + if (this->_arrayIsOpen) { + this->reportWarning(type + " value is not allowed in the integer array and has been replaced with a default value."); + this->_pArray->emplace_back(); + return this->ignoreAndContinue(); + } else { + this->reportWarning(type + " is not allowed and has been ignored."); + return this->ignoreAndReturnToParent(); + } + } + + std::vector* _pArray = nullptr; + bool _arrayIsOpen = false; + }; + + template <> + class ArrayJsonHandler : public JsonHandler { + public: + void reset(IJsonHandler* pParent, std::vector* pArray) { + JsonHandler::reset(pParent); + this->_pArray = pArray; + this->_arrayIsOpen = false; + } + + virtual IJsonHandler* Null() override { + return this->invalid("A null")->Null(); + } + + virtual IJsonHandler* Bool(bool b) override { + return this->invalid("A bool")->Bool(b); + } + + virtual IJsonHandler* Int(int i) override { + return this->invalid("An integer")->Int(i); + } + + virtual IJsonHandler* Uint(unsigned i) override { + return this->invalid("An integer")->Uint(i); + } + + virtual IJsonHandler* Int64(int64_t i) override { + return this->invalid("An integer")->Int64(i); + } + + virtual IJsonHandler* Uint64(uint64_t i) override { + return this->invalid("An integer")->Uint64(i); + } + + virtual IJsonHandler* Double(double d) override { + return this->invalid("A double (floating-point)")->Double(d); + } + + virtual IJsonHandler* StartObject() override { + return this->invalid("An object")->StartObject(); + } + + virtual IJsonHandler* StartArray() override { + if (this->_arrayIsOpen) { + return this->invalid("An array")->StartArray(); + } + + this->_arrayIsOpen = true; + this->_pArray->clear(); + return this; + } + + virtual IJsonHandler* EndArray(size_t) override { + return this->parent(); + } + + virtual IJsonHandler* String(const char* str, size_t length, bool copy) override { + if (!this->_arrayIsOpen) { + return this->invalid("A string")->String(str, length, copy); + } + + assert(this->_pArray); + this->_pArray->emplace_back(std::string(str, length)); + return this; + } + + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override { + context.push_back(std::string("[") + std::to_string(this->_pArray->size()) + "]"); + this->parent()->reportWarning(warning, std::move(context)); + } + + private: + IJsonHandler* invalid(const std::string& type) { + if (this->_arrayIsOpen) { + this->reportWarning(type + " value is not allowed in the string array and has been replaced with a default value."); + this->_pArray->emplace_back(); + return this->ignoreAndContinue(); + } else { + this->reportWarning(type + " is not allowed and has been ignored."); + return this->ignoreAndReturnToParent(); + } + } + + std::vector* _pArray = nullptr; + bool _arrayIsOpen = false; + }; +} diff --git a/CesiumGltfReader/src/BoolJsonHandler.cpp b/CesiumGltfReader/src/BoolJsonHandler.cpp new file mode 100644 index 000000000..3a2292a32 --- /dev/null +++ b/CesiumGltfReader/src/BoolJsonHandler.cpp @@ -0,0 +1,15 @@ +#include "BoolJsonHandler.h" +#include + +using namespace CesiumGltf; + +void BoolJsonHandler::reset(IJsonHandler* pParent, bool* pBool) { + JsonHandler::reset(pParent); + this->_pBool = pBool; +} + +IJsonHandler* BoolJsonHandler::Bool(bool b) { + assert(this->_pBool); + *this->_pBool = b; + return this->parent(); +} diff --git a/CesiumGltfReader/src/BoolJsonHandler.h b/CesiumGltfReader/src/BoolJsonHandler.h new file mode 100644 index 000000000..c6eb81dd6 --- /dev/null +++ b/CesiumGltfReader/src/BoolJsonHandler.h @@ -0,0 +1,15 @@ +#pragma once + +#include "JsonHandler.h" + +namespace CesiumGltf { + class BoolJsonHandler : public JsonHandler { + public: + void reset(IJsonHandler* pParent, bool* pBool); + + virtual IJsonHandler* Bool(bool b) override; + + private: + bool* _pBool = nullptr; + }; +} diff --git a/CesiumGltfReader/src/DictionaryJsonHandler.h b/CesiumGltfReader/src/DictionaryJsonHandler.h new file mode 100644 index 000000000..ef83fbe54 --- /dev/null +++ b/CesiumGltfReader/src/DictionaryJsonHandler.h @@ -0,0 +1,49 @@ +#pragma once + +#include "ObjectJsonHandler.h" +#include "IntegerJsonHandler.h" +#include +#include + +namespace CesiumGltf { + template + class DictionaryJsonHandler : public ObjectJsonHandler { + public: + void reset(IJsonHandler* pParent, std::unordered_map* pDictionary) { + ObjectJsonHandler::reset(pParent); + this->_pDictionary1 = pDictionary; + } + + void reset(IJsonHandler* pParent, std::map* pDictionary) { + ObjectJsonHandler::reset(pParent); + this->_pDictionary2 = pDictionary; + } + + virtual IJsonHandler* Key(const char* str, size_t /*length*/, bool /*copy*/) override { + assert(this->_pDictionary1 || this->_pDictionary2); + + if (this->_pDictionary1) { + auto it = this->_pDictionary1->emplace(str, T()).first; + + return this->property( + it->first.c_str(), + this->_item, + it->second + ); + } else { + auto it = this->_pDictionary2->emplace(str, T()).first; + + return this->property( + it->first.c_str(), + this->_item, + it->second + ); + } + } + + private: + std::unordered_map* _pDictionary1 = nullptr; + std::map* _pDictionary2 = nullptr; + THandler _item; + }; +} diff --git a/CesiumGltfReader/src/DoubleJsonHandler.cpp b/CesiumGltfReader/src/DoubleJsonHandler.cpp new file mode 100644 index 000000000..a6b2a1252 --- /dev/null +++ b/CesiumGltfReader/src/DoubleJsonHandler.cpp @@ -0,0 +1,38 @@ +#include "DoubleJsonHandler.h" + +using namespace CesiumGltf; + +void DoubleJsonHandler::reset(IJsonHandler* pParent, double* pDouble) { + JsonHandler::reset(pParent); + this->_pDouble = pDouble; +} + +IJsonHandler* DoubleJsonHandler::Int(int i) { + assert(this->_pDouble); + *this->_pDouble = static_cast(i); + return this->parent(); +} + +IJsonHandler* DoubleJsonHandler::Uint(unsigned i) { + assert(this->_pDouble); + *this->_pDouble = static_cast(i); + return this->parent(); +} + +IJsonHandler* DoubleJsonHandler::Int64(int64_t i) { + assert(this->_pDouble); + *this->_pDouble = static_cast(i); + return this->parent(); +} + +IJsonHandler* DoubleJsonHandler::Uint64(uint64_t i) { + assert(this->_pDouble); + *this->_pDouble = static_cast(i); + return this->parent(); +} + +IJsonHandler* DoubleJsonHandler::Double(double d) { + assert(this->_pDouble); + *this->_pDouble = d; + return this->parent(); +} diff --git a/CesiumGltfReader/src/DoubleJsonHandler.h b/CesiumGltfReader/src/DoubleJsonHandler.h new file mode 100644 index 000000000..ee24de2cd --- /dev/null +++ b/CesiumGltfReader/src/DoubleJsonHandler.h @@ -0,0 +1,20 @@ +#pragma once + +#include "JsonHandler.h" +#include + +namespace CesiumGltf { + class DoubleJsonHandler : public JsonHandler { + public: + void reset(IJsonHandler* pParent, double* pDouble); + + virtual IJsonHandler* Int(int i) override; + virtual IJsonHandler* Uint(unsigned i) override; + virtual IJsonHandler* Int64(int64_t i) override; + virtual IJsonHandler* Uint64(uint64_t i) override; + virtual IJsonHandler* Double(double d) override; + + private: + double* _pDouble = nullptr; + }; +} diff --git a/CesiumGltfReader/src/ExtensibleObjectJsonHandler.cpp b/CesiumGltfReader/src/ExtensibleObjectJsonHandler.cpp new file mode 100644 index 000000000..053ddf3f9 --- /dev/null +++ b/CesiumGltfReader/src/ExtensibleObjectJsonHandler.cpp @@ -0,0 +1,16 @@ +#include "ExtensibleObjectJsonHandler.h" +#include "CesiumGltf/ExtensibleObject.h" + +using namespace CesiumGltf; + +void ExtensibleObjectJsonHandler::reset(IJsonHandler* pParent, ExtensibleObject* /*pObject*/) { + ObjectJsonHandler::reset(pParent); +} + +IJsonHandler* ExtensibleObjectJsonHandler::ExtensibleObjectKey(const char* str, ExtensibleObject& o) { + using namespace std::string_literals; + + if ("extras"s == str) return property("extras", this->_extras, o.extras); + + return this->ignoreAndContinue(); +} \ No newline at end of file diff --git a/CesiumGltfReader/src/ExtensibleObjectJsonHandler.h b/CesiumGltfReader/src/ExtensibleObjectJsonHandler.h new file mode 100644 index 000000000..53a0d185a --- /dev/null +++ b/CesiumGltfReader/src/ExtensibleObjectJsonHandler.h @@ -0,0 +1,18 @@ +#pragma once + +#include "ObjectJsonHandler.h" +#include "DictionaryJsonHandler.h" +#include "JsonObjectJsonHandler.h" + +namespace CesiumGltf { + struct ExtensibleObject; + + class ExtensibleObjectJsonHandler : public ObjectJsonHandler { + protected: + void reset(IJsonHandler* pParent, ExtensibleObject* pObject); + IJsonHandler* ExtensibleObjectKey(const char* str, ExtensibleObject& o); + + private: + DictionaryJsonHandler _extras; + }; +} diff --git a/CesiumGltfReader/src/IJsonHandler.h b/CesiumGltfReader/src/IJsonHandler.h new file mode 100644 index 000000000..e93ff5314 --- /dev/null +++ b/CesiumGltfReader/src/IJsonHandler.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include + +namespace CesiumGltf { + class IJsonHandler { + public: + virtual IJsonHandler* Null() = 0; + virtual IJsonHandler* Bool(bool b) = 0; + virtual IJsonHandler* Int(int i) = 0; + virtual IJsonHandler* Uint(unsigned i) = 0; + virtual IJsonHandler* Int64(int64_t i) = 0; + virtual IJsonHandler* Uint64(uint64_t i) = 0; + virtual IJsonHandler* Double(double d) = 0; + virtual IJsonHandler* RawNumber(const char* str, size_t length, bool copy) = 0; + virtual IJsonHandler* String(const char* str, size_t length, bool copy) = 0; + virtual IJsonHandler* StartObject() = 0; + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) = 0; + virtual IJsonHandler* EndObject(size_t memberCount) = 0; + virtual IJsonHandler* StartArray() = 0; + virtual IJsonHandler* EndArray(size_t elementCount) = 0; + + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) = 0; + }; +} diff --git a/CesiumGltfReader/src/IgnoreValueJsonHandler.cpp b/CesiumGltfReader/src/IgnoreValueJsonHandler.cpp new file mode 100644 index 000000000..9f8bb72d0 --- /dev/null +++ b/CesiumGltfReader/src/IgnoreValueJsonHandler.cpp @@ -0,0 +1,73 @@ +#include "IgnoreValueJsonHandler.h" + +using namespace CesiumGltf; + +void IgnoreValueJsonHandler::reset(IJsonHandler* pParent) { + this->_pParent = pParent; + this->_depth = 0; +} + +IJsonHandler* IgnoreValueJsonHandler::Null() { + return this->_depth == 0 ? this->parent() : this; +} + +IJsonHandler* IgnoreValueJsonHandler::Bool(bool /*b*/) { + return this->_depth == 0 ? this->parent() : this; +} + +IJsonHandler* IgnoreValueJsonHandler::Int(int /*i*/) { + return this->_depth == 0 ? this->parent() : this; +} + +IJsonHandler* IgnoreValueJsonHandler::Uint(unsigned /*i*/) { + return this->_depth == 0 ? this->parent() : this; +} + +IJsonHandler* IgnoreValueJsonHandler::Int64(int64_t /*i*/) { + return this->_depth == 0 ? this->parent() : this; +} + +IJsonHandler* IgnoreValueJsonHandler::Uint64(uint64_t /*i*/) { + return this->_depth == 0 ? this->parent() : this; +} + +IJsonHandler* IgnoreValueJsonHandler::Double(double /*d*/) { + return this->_depth == 0 ? this->parent() : this; +} + +IJsonHandler* IgnoreValueJsonHandler::RawNumber(const char* /*str*/, size_t /*length*/, bool /*copy*/) { + return this->_depth == 0 ? this->parent() : this; +} + +IJsonHandler* IgnoreValueJsonHandler::String(const char* /*str*/, size_t /*length*/, bool /*copy*/) { + return this->_depth == 0 ? this->parent() : this; +} + +IJsonHandler* IgnoreValueJsonHandler::StartObject() { + ++this->_depth; return this; +} + +IJsonHandler* IgnoreValueJsonHandler::Key(const char* /*str*/, size_t /*length*/, bool /*copy*/) { + return this; +} + +IJsonHandler* IgnoreValueJsonHandler::EndObject(size_t /*memberCount*/) { + --this->_depth; return this->_depth == 0 ? this->parent() : this; +} + +IJsonHandler* IgnoreValueJsonHandler::StartArray() { + ++this->_depth; return this; +} + +IJsonHandler* IgnoreValueJsonHandler::EndArray(size_t /*elementCount*/) { + --this->_depth; return this->_depth == 0 ? this->parent() : this; +} + +void IgnoreValueJsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + context.push_back("Ignoring a value"); + this->parent()->reportWarning(warning, std::move(context)); +} + +IJsonHandler* IgnoreValueJsonHandler::parent() { + return this->_pParent; +} \ No newline at end of file diff --git a/CesiumGltfReader/src/IgnoreValueJsonHandler.h b/CesiumGltfReader/src/IgnoreValueJsonHandler.h new file mode 100644 index 000000000..0e62e9175 --- /dev/null +++ b/CesiumGltfReader/src/IgnoreValueJsonHandler.h @@ -0,0 +1,34 @@ +#pragma once + +#include "IJsonHandler.h" +#include + +namespace CesiumGltf { + class IgnoreValueJsonHandler : public IJsonHandler { + public: + void reset(IJsonHandler* pParent); + + virtual IJsonHandler* Null() override; + virtual IJsonHandler* Bool(bool b) override; + virtual IJsonHandler* Int(int i) override; + virtual IJsonHandler* Uint(unsigned i) override; + virtual IJsonHandler* Int64(int64_t i) override; + virtual IJsonHandler* Uint64(uint64_t i) override; + virtual IJsonHandler* Double(double d) override; + virtual IJsonHandler* RawNumber(const char* str, size_t length, bool copy) override; + virtual IJsonHandler* String(const char* str, size_t length, bool copy) override; + virtual IJsonHandler* StartObject() override; + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + virtual IJsonHandler* EndObject(size_t memberCount) override; + virtual IJsonHandler* StartArray() override; + virtual IJsonHandler* EndArray(size_t elementCount) override; + + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + IJsonHandler* parent(); + + private: + IJsonHandler* _pParent = nullptr; + int32_t _depth = 0; + }; +} diff --git a/CesiumGltfReader/src/IntegerJsonHandler.h b/CesiumGltfReader/src/IntegerJsonHandler.h new file mode 100644 index 000000000..05c5d0905 --- /dev/null +++ b/CesiumGltfReader/src/IntegerJsonHandler.h @@ -0,0 +1,46 @@ +#pragma once + +#include "JsonHandler.h" +#include + +namespace CesiumGltf { + template + class IntegerJsonHandler : public JsonHandler { + public: + void reset(IJsonHandler* pParent, T* pInteger) { + JsonHandler::reset(pParent); + this->_pInteger = pInteger; + } + + T* getObject() { return this->_pInteger; } + + virtual IJsonHandler* Int(int i) override { + assert(this->_pInteger); + *this->_pInteger = static_cast(i); + return this->parent(); + } + virtual IJsonHandler* Uint(unsigned i) override { + assert(this->_pInteger); + *this->_pInteger = static_cast(i); + return this->parent(); + } + virtual IJsonHandler* Int64(int64_t i) override { + assert(this->_pInteger); + *this->_pInteger = static_cast(i); + return this->parent(); + } + virtual IJsonHandler* Uint64(uint64_t i) override { + assert(this->_pInteger); + *this->_pInteger = static_cast(i); + return this->parent(); + } + + virtual void reportWarning(const std::string& warning, std::vector&& context) override { + context.push_back("(expecting an integer)"); + this->parent()->reportWarning(warning, std::move(context)); + } + + private: + T* _pInteger = nullptr; + }; +} diff --git a/CesiumGltfReader/src/JsonHandler.cpp b/CesiumGltfReader/src/JsonHandler.cpp new file mode 100644 index 000000000..9335e525b --- /dev/null +++ b/CesiumGltfReader/src/JsonHandler.cpp @@ -0,0 +1,92 @@ +#include "JsonHandler.h" + +using namespace CesiumGltf; + +IJsonHandler* JsonHandler::Null() { + this->reportWarning("A null value is not allowed and has been ignored."); + return this->parent(); +} + +IJsonHandler* JsonHandler::Bool(bool /*b*/) { + this->reportWarning("A boolean value is not allowed and has been ignored."); + return this->parent(); +} + +IJsonHandler* JsonHandler::Int(int /*i*/) { + this->reportWarning("An integer value is not allowed and has been ignored."); + return this->parent(); +} + +IJsonHandler* JsonHandler::Uint(unsigned /*i*/) { + this->reportWarning("An integer value is not allowed and has been ignored."); + return this->parent(); +} + +IJsonHandler* JsonHandler::Int64(int64_t /*i*/) { + this->reportWarning("An integer value is not allowed and has been ignored."); + return this->parent(); +} + +IJsonHandler* JsonHandler::Uint64(uint64_t /*i*/) { + this->reportWarning("An integer value is not allowed and has been ignored."); + return this->parent(); +} + +IJsonHandler* JsonHandler::Double(double /*d*/) { + this->reportWarning("A double value is not allowed and has been ignored."); + return this->parent(); +} + +IJsonHandler* JsonHandler::RawNumber(const char* /*str*/, size_t /*length*/, bool /*copy*/) { + this->reportWarning("A numeric value is not allowed and has been ignored."); + return this->parent(); +} + +IJsonHandler* JsonHandler::String(const char* /*str*/, size_t /*length*/, bool /*copy*/) { + this->reportWarning("A string value is not allowed and has been ignored."); + return this->parent(); +} + +IJsonHandler* JsonHandler::StartObject() { + this->reportWarning("An object value is not allowed and has been ignored."); + return this->ignoreAndReturnToParent()->StartObject(); +} + +IJsonHandler* JsonHandler::Key(const char* /*str*/, size_t /*length*/, bool /*copy*/) { + return nullptr; +} + +IJsonHandler* JsonHandler::EndObject(size_t /*memberCount*/) { + return nullptr; +} + +IJsonHandler* JsonHandler::StartArray() { + this->reportWarning("An array value is not allowed and has been ignored."); + return this->ignoreAndReturnToParent()->StartArray(); +} + +IJsonHandler* JsonHandler::EndArray(size_t /*elementCount*/) { + return nullptr; +} + +IJsonHandler* JsonHandler::parent() { + return this->_pParent; +} + +IJsonHandler* JsonHandler::ignoreAndReturnToParent() { + this->_ignore.reset(this->parent()); + return &this->_ignore; +} + +IJsonHandler* JsonHandler::ignoreAndContinue() { + this->_ignore.reset(this); + return &this->_ignore; +} + +void JsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + this->parent()->reportWarning(warning, std::move(context)); +} + +void JsonHandler::reset(IJsonHandler* pParent) { + this->_pParent = pParent; +} diff --git a/CesiumGltfReader/src/JsonHandler.h b/CesiumGltfReader/src/JsonHandler.h new file mode 100644 index 000000000..eb31bc01b --- /dev/null +++ b/CesiumGltfReader/src/JsonHandler.h @@ -0,0 +1,48 @@ +#pragma once + +#include "IJsonHandler.h" +#include "IgnoreValueJsonHandler.h" +#include +#include + +namespace CesiumGltf { + class JsonHandler : public IJsonHandler { + public: + virtual IJsonHandler* Null() override; + virtual IJsonHandler* Bool(bool b) override; + virtual IJsonHandler* Int(int i) override; + virtual IJsonHandler* Uint(unsigned i) override; + virtual IJsonHandler* Int64(int64_t i) override; + virtual IJsonHandler* Uint64(uint64_t i) override; + virtual IJsonHandler* Double(double d) override; + virtual IJsonHandler* RawNumber(const char* str, size_t length, bool copy) override; + virtual IJsonHandler* String(const char* str, size_t length, bool copy) override; + virtual IJsonHandler* StartObject() override; + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + virtual IJsonHandler* EndObject(size_t memberCount) override; + virtual IJsonHandler* StartArray() override; + virtual IJsonHandler* EndArray(size_t elementCount) override; + + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + protected: + void reset(IJsonHandler* pParent); + + IJsonHandler* parent(); + + /** + * @brief Ignore a single value and then return to the parent handler. + */ + IJsonHandler* ignoreAndReturnToParent(); + + /** + * @brief Ignore a single value and the continue processing more tokens with this handler. + */ + IJsonHandler* ignoreAndContinue(); + + private: + IJsonHandler* _pParent = nullptr; + IgnoreValueJsonHandler _ignore; + }; + +} diff --git a/CesiumGltfReader/src/JsonObjectJsonHandler.cpp b/CesiumGltfReader/src/JsonObjectJsonHandler.cpp new file mode 100644 index 000000000..860ce5466 --- /dev/null +++ b/CesiumGltfReader/src/JsonObjectJsonHandler.cpp @@ -0,0 +1,114 @@ +#include "JsonObjectJsonHandler.h" + +using namespace CesiumGltf; + +namespace { + template + void addOrReplace(JsonValue& json, T value) { + JsonValue::Array* pArray = std::get_if(&json.value); + if (pArray) { + pArray->emplace_back(value); + } else { + json = value; + } + } +} + +void JsonObjectJsonHandler::reset(IJsonHandler* pParent, JsonValue* pValue) { + JsonHandler::reset(pParent); + this->_stack.clear(); + this->_stack.push_back(pValue); +} + +IJsonHandler* JsonObjectJsonHandler::Null() { + addOrReplace(*this->_stack.back(), JsonValue::Null()); + return this->doneElement(); +} + +IJsonHandler* JsonObjectJsonHandler::Bool(bool b) { + addOrReplace(*this->_stack.back(), b); + return this->doneElement(); +} + +IJsonHandler* JsonObjectJsonHandler::Int(int i) { + addOrReplace(*this->_stack.back(), JsonValue::Number(i)); + return this->doneElement(); +} + +IJsonHandler* JsonObjectJsonHandler::Uint(unsigned i) { + addOrReplace(*this->_stack.back(), JsonValue::Number(i)); + return this->doneElement(); +} + +IJsonHandler* JsonObjectJsonHandler::Int64(int64_t i) { + addOrReplace(*this->_stack.back(), JsonValue::Number(i)); + return this->doneElement(); +} + +IJsonHandler* JsonObjectJsonHandler::Uint64(uint64_t i) { + addOrReplace(*this->_stack.back(), JsonValue::Number(i)); + return this->doneElement(); +} + +IJsonHandler* JsonObjectJsonHandler::Double(double d) { + addOrReplace(*this->_stack.back(), d); + return this->doneElement(); +} + +IJsonHandler* JsonObjectJsonHandler::RawNumber(const char* /* str */, size_t /* length */, bool /* copy */) { + return nullptr; +} + +IJsonHandler* JsonObjectJsonHandler::String(const char* str, size_t /* length */, bool /* copy */) { + *this->_stack.back() = str; + return this->doneElement(); +} + +IJsonHandler* JsonObjectJsonHandler::StartObject() { + JsonValue& current = *this->_stack.back(); + JsonValue::Array* pArray = std::get_if(¤t.value); + if (pArray) { + JsonValue& newArray = pArray->emplace_back(JsonValue::Object()); + this->_stack.emplace_back(&newArray); + } else { + current = JsonValue::Object(); + } + + return this; +} + +IJsonHandler* JsonObjectJsonHandler::Key(const char* str, size_t /* length */, bool /* copy */) { + JsonValue& json = *this->_stack.back(); + JsonValue::Object* pObject = std::get_if(&json.value); + + auto it = pObject->emplace(str, JsonValue()).first; + this->_stack.push_back(&it->second); + this->_currentKey = str; + return this; +} + +IJsonHandler* JsonObjectJsonHandler::EndObject(size_t /* memberCount */) { + return this->doneElement(); +} + +IJsonHandler* JsonObjectJsonHandler::StartArray() { + JsonValue& current = *this->_stack.back(); + JsonValue::Array* pArray = std::get_if(¤t.value); + if (pArray) { + JsonValue& newArray = pArray->emplace_back(JsonValue::Array()); + this->_stack.emplace_back(&newArray); + } else { + current = JsonValue::Array(); + } + + return this; +} + +IJsonHandler* JsonObjectJsonHandler::EndArray(size_t /* elementCount */) { + return this->doneElement(); +} + +IJsonHandler* JsonObjectJsonHandler::doneElement() { + this->_stack.pop_back(); + return this->_stack.empty() ? this->parent() : this; +} \ No newline at end of file diff --git a/CesiumGltfReader/src/JsonObjectJsonHandler.h b/CesiumGltfReader/src/JsonObjectJsonHandler.h new file mode 100644 index 000000000..38719824e --- /dev/null +++ b/CesiumGltfReader/src/JsonObjectJsonHandler.h @@ -0,0 +1,34 @@ +#pragma once + +#include "CesiumGltf/JsonValue.h" +#include "JsonHandler.h" + +namespace CesiumGltf { + + class JsonObjectJsonHandler : public JsonHandler { + public: + void reset(IJsonHandler* pParent, JsonValue* pValue); + + virtual IJsonHandler* Null() override; + virtual IJsonHandler* Bool(bool b) override; + virtual IJsonHandler* Int(int i) override; + virtual IJsonHandler* Uint(unsigned i) override; + virtual IJsonHandler* Int64(int64_t i) override; + virtual IJsonHandler* Uint64(uint64_t i) override; + virtual IJsonHandler* Double(double d) override; + virtual IJsonHandler* RawNumber(const char* str, size_t length, bool copy) override; + virtual IJsonHandler* String(const char* str, size_t length, bool copy) override; + virtual IJsonHandler* StartObject() override; + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + virtual IJsonHandler* EndObject(size_t memberCount) override; + virtual IJsonHandler* StartArray() override; + virtual IJsonHandler* EndArray(size_t elementCount) override; + + private: + IJsonHandler* doneElement(); + + std::vector _stack; + const char* _currentKey; + }; + +} diff --git a/CesiumGltfReader/src/NamedObjectJsonHandler.cpp b/CesiumGltfReader/src/NamedObjectJsonHandler.cpp new file mode 100644 index 000000000..f18a1d1c8 --- /dev/null +++ b/CesiumGltfReader/src/NamedObjectJsonHandler.cpp @@ -0,0 +1,15 @@ +#include "NamedObjectJsonHandler.h" +#include "CesiumGltf/NamedObject.h" +#include + +using namespace CesiumGltf; + +void NamedObjectJsonHandler::reset(IJsonHandler* pParent, NamedObject* pObject) { + ExtensibleObjectJsonHandler::reset(pParent, pObject); +} + +IJsonHandler* NamedObjectJsonHandler::NamedObjectKey(const char* str, NamedObject& o) { + using namespace std::string_literals; + if ("name"s == str) return property("name", this->_name, o.name); + return this->ExtensibleObjectKey(str, o); +} diff --git a/CesiumGltfReader/src/NamedObjectJsonHandler.h b/CesiumGltfReader/src/NamedObjectJsonHandler.h new file mode 100644 index 000000000..fbb89eb7d --- /dev/null +++ b/CesiumGltfReader/src/NamedObjectJsonHandler.h @@ -0,0 +1,17 @@ +#pragma once + +#include "ExtensibleObjectJsonHandler.h" +#include "StringJsonHandler.h" + +namespace CesiumGltf { + struct NamedObject; + + class NamedObjectJsonHandler : public ExtensibleObjectJsonHandler { + protected: + void reset(IJsonHandler* pParent, NamedObject* pObject); + IJsonHandler* NamedObjectKey(const char* str, NamedObject& o); + + private: + StringJsonHandler _name; + }; +} diff --git a/CesiumGltfReader/src/ObjectJsonHandler.cpp b/CesiumGltfReader/src/ObjectJsonHandler.cpp new file mode 100644 index 000000000..098b3732f --- /dev/null +++ b/CesiumGltfReader/src/ObjectJsonHandler.cpp @@ -0,0 +1,34 @@ +#include "ObjectJsonHandler.h" + +using namespace CesiumGltf; + +IJsonHandler* ObjectJsonHandler::StartObject() { + ++this->_depth; + if (this->_depth > 1) { + return this->StartSubObject(); + } + return this; +} + +IJsonHandler* ObjectJsonHandler::EndObject(size_t memberCount) { + this->_currentKey = nullptr; + + --this->_depth; + + if (this->_depth > 0) + return this->EndSubObject(memberCount); + else + return this->parent(); +} + +IJsonHandler* ObjectJsonHandler::StartSubObject() { + return nullptr; +} + +IJsonHandler* ObjectJsonHandler::EndSubObject(size_t /*memberCount*/) { + return nullptr; +} + +const char* ObjectJsonHandler::getCurrentKey() const { + return this->_currentKey; +} diff --git a/CesiumGltfReader/src/ObjectJsonHandler.h b/CesiumGltfReader/src/ObjectJsonHandler.h new file mode 100644 index 000000000..23f846a2b --- /dev/null +++ b/CesiumGltfReader/src/ObjectJsonHandler.h @@ -0,0 +1,46 @@ +#pragma once + +#include "JsonHandler.h" +#include + +namespace CesiumGltf { + class ObjectJsonHandler : public JsonHandler { + public: + virtual IJsonHandler* StartObject() override final; + virtual IJsonHandler* EndObject(size_t memberCount) override final; + + protected: + virtual IJsonHandler* StartSubObject(); + virtual IJsonHandler* EndSubObject(size_t memberCount); + + template + IJsonHandler* property(const char* currentKey, TAccessor& accessor, TProperty& value) { + this->_currentKey = currentKey; + + if constexpr (isOptional::value) { + value.emplace(); + accessor.reset(this, &value.value()); + } else { + accessor.reset(this, &value); + } + + return &accessor; + } + + const char* getCurrentKey() const; + + private: + template + struct isOptional { + static constexpr bool value = false; + }; + + template + struct isOptional> { + static constexpr bool value = true; + }; + + int32_t _depth = 0; + const char* _currentKey; + }; +} diff --git a/CesiumGltfReader/src/Reader.cpp b/CesiumGltfReader/src/Reader.cpp new file mode 100644 index 000000000..1e2c4e017 --- /dev/null +++ b/CesiumGltfReader/src/Reader.cpp @@ -0,0 +1,337 @@ +#include "CesiumGltf/Reader.h" +#include "decodeDataUrls.h" +#include "decodeDraco.h" +#include "JsonHandler.h" +#include "ModelJsonHandler.h" +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#define STBI_FAILURE_USERMSG +#include + +using namespace CesiumGltf; + +namespace { + struct Dispatcher { + IJsonHandler* pCurrent; + + bool update(IJsonHandler* pNext) { + if (pNext == nullptr) { + return false; + } + + this->pCurrent = pNext; + return true; + } + + bool Null() { return update(pCurrent->Null()); } + bool Bool(bool b) { return update(pCurrent->Bool(b)); } + bool Int(int i) { return update(pCurrent->Int(i)); } + bool Uint(unsigned i) { return update(pCurrent->Uint(i)); } + bool Int64(int64_t i) { return update(pCurrent->Int64(i)); } + bool Uint64(uint64_t i) { return update(pCurrent->Uint64(i)); } + bool Double(double d) { return update(pCurrent->Double(d)); } + bool RawNumber(const char* str, size_t length, bool copy) { return update(pCurrent->RawNumber(str, length, copy)); } + bool String(const char* str, size_t length, bool copy) { return update(pCurrent->String(str, length, copy)); } + bool StartObject() { return update(pCurrent->StartObject()); } + bool Key(const char* str, size_t length, bool copy) { return update(pCurrent->Key(str, length, copy)); } + bool EndObject(size_t memberCount) { return update(pCurrent->EndObject(memberCount)); } + bool StartArray() { return update(pCurrent->StartArray()); } + bool EndArray(size_t elementCount) { return update(pCurrent->EndArray(elementCount)); } + }; + + class FinalJsonHandler : public ObjectJsonHandler { + public: + FinalJsonHandler(ModelReaderResult& result, rapidjson::MemoryStream& inputStream) : + _result(result), + _inputStream(inputStream) + { + reset(this); + } + + virtual void reportWarning(const std::string& warning, std::vector&& context) override { + std::string fullWarning = warning; + fullWarning += "\n While parsing: "; + for (auto it = context.rbegin(); it != context.rend(); ++it) { + fullWarning += *it; + } + + fullWarning += "\n From byte offset: "; + fullWarning += std::to_string(this->_inputStream.Tell()); + + this->_result.warnings.emplace_back(std::move(fullWarning)); + } + + private: + ModelReaderResult& _result; + rapidjson::MemoryStream& _inputStream; + }; + + std::string getMessageFromRapidJsonError(rapidjson::ParseErrorCode code) { + switch (code) { + case rapidjson::ParseErrorCode::kParseErrorDocumentEmpty: + return "The document is empty."; + case rapidjson::ParseErrorCode::kParseErrorDocumentRootNotSingular: + return "The document root must not be followed by other values."; + case rapidjson::ParseErrorCode::kParseErrorValueInvalid: + return "Invalid value."; + case rapidjson::ParseErrorCode::kParseErrorObjectMissName: + return "Missing a name for object member."; + case rapidjson::ParseErrorCode::kParseErrorObjectMissColon: + return "Missing a colon after a name of object member."; + case rapidjson::ParseErrorCode::kParseErrorObjectMissCommaOrCurlyBracket: + return "Missing a comma or '}' after an object member."; + case rapidjson::ParseErrorCode::kParseErrorArrayMissCommaOrSquareBracket: + return "Missing a comma or ']' after an array element."; + case rapidjson::ParseErrorCode::kParseErrorStringUnicodeEscapeInvalidHex: + return "Incorrect hex digit after \\u escape in string."; + case rapidjson::ParseErrorCode::kParseErrorStringUnicodeSurrogateInvalid: + return "The surrogate pair in string is invalid."; + case rapidjson::ParseErrorCode::kParseErrorStringEscapeInvalid: + return "Invalid escape character in string."; + case rapidjson::ParseErrorCode::kParseErrorStringMissQuotationMark: + return "Missing a closing quotation mark in string."; + case rapidjson::ParseErrorCode::kParseErrorStringInvalidEncoding: + return "Invalid encoding in string."; + case rapidjson::ParseErrorCode::kParseErrorNumberTooBig: + return "Number too big to be stored in double."; + case rapidjson::ParseErrorCode::kParseErrorNumberMissFraction: + return "Missing fraction part in number."; + case rapidjson::ParseErrorCode::kParseErrorNumberMissExponent: + return "Missing exponent in number."; + case rapidjson::ParseErrorCode::kParseErrorTermination: + return "Parsing was terminated."; + case rapidjson::ParseErrorCode::kParseErrorUnspecificSyntaxError: + default: + return "Unspecific syntax error."; + } + } + + #pragma pack(push, 1) + struct GlbHeader { + uint32_t magic; + uint32_t version; + uint32_t length; + }; + + struct ChunkHeader { + uint32_t chunkLength; + uint32_t chunkType; + }; + #pragma pack(pop) + + bool isBinaryGltf(const gsl::span& data) { + if (data.size() < sizeof(GlbHeader)) { + return false; + } + + return reinterpret_cast(data.data())->magic == 0x46546C67; + } + + ModelReaderResult readJsonModel(const gsl::span& data) { + rapidjson::Reader reader; + rapidjson::MemoryStream inputStream(reinterpret_cast(data.data()), data.size()); + + ModelReaderResult result; + ModelJsonHandler modelHandler; + FinalJsonHandler finalHandler(result, inputStream); + Dispatcher dispatcher { &modelHandler }; + + result.model.emplace(); + modelHandler.reset(&finalHandler, &result.model.value()); + + reader.IterativeParseInit(); + + bool success = true; + while (success && !reader.IterativeParseComplete()) { + success = reader.IterativeParseNext(inputStream, dispatcher); + } + + if (reader.HasParseError()) { + result.model.reset(); + + std::string s("glTF JSON parsing error at byte offset "); + s += std::to_string(reader.GetErrorOffset()); + s += ": "; + s += getMessageFromRapidJsonError(reader.GetParseErrorCode()); + result.errors.emplace_back(std::move(s)); + } + + return result; + } + + ModelReaderResult readBinaryModel(const gsl::span& data) { + if (data.size() < sizeof(GlbHeader) + sizeof(ChunkHeader)) { + return { + std::nullopt, + {"Too short to be a valid GLB."}, + {} + }; + } + + const GlbHeader* pHeader = reinterpret_cast(data.data()); + if (pHeader->magic != 0x46546C67) { + return { + std::nullopt, + { "GLB does not start with the expected magic value 'glTF'." }, + {} + }; + } + + if (pHeader->version != 2) { + return { + std::nullopt, + { "Only binary glTF version 2 is supported." }, + {} + }; + } + + if (pHeader->length > data.size()) { + return { + std::nullopt, + { "GLB extends past the end of the buffer." }, + {} + }; + } + + gsl::span glbData = data.subspan(0, pHeader->length); + + const ChunkHeader* pJsonChunkHeader = reinterpret_cast(glbData.data() + sizeof(GlbHeader)); + if (pJsonChunkHeader->chunkType != 0x4E4F534A) { + return { + std::nullopt, + { "GLB JSON chunk does not have the expected chunkType." }, + {} + }; + } + + size_t jsonStart = sizeof(GlbHeader) + sizeof(ChunkHeader); + size_t jsonEnd = jsonStart + pJsonChunkHeader->chunkLength; + + if (jsonEnd > glbData.size()) { + return { + std::nullopt, + { "GLB JSON chunk extends past the end of the buffer." }, + {} + }; + } + + gsl::span jsonChunk = glbData.subspan(jsonStart, pJsonChunkHeader->chunkLength); + gsl::span binaryChunk; + + if (jsonEnd + sizeof(ChunkHeader) <= data.size()) { + const ChunkHeader* pBinaryChunkHeader = reinterpret_cast(glbData.data() + jsonEnd); + if (pBinaryChunkHeader->chunkType != 0x004E4942) { + return { + std::nullopt, + { "GLB binary chunk does not have the expected chunkType." }, + {} + }; + } + + size_t binaryStart = jsonEnd + sizeof(ChunkHeader); + size_t binaryEnd = binaryStart + pBinaryChunkHeader->chunkLength; + + if (binaryEnd > glbData.size()) { + return { + std::nullopt, + { "GLB binary chunk extends past the end of the buffer." }, + {} + }; + } + + binaryChunk = glbData.subspan(binaryStart, pBinaryChunkHeader->chunkLength); + } + + ModelReaderResult result = readJsonModel(jsonChunk); + + if (result.model && !binaryChunk.empty()) { + Model& model = result.model.value(); + + if (model.buffers.size() == 0) { + result.errors.emplace_back("GLB has a binary chunk but the JSON does not define any buffers."); + return result; + } + + Buffer& buffer = model.buffers[0]; + if (buffer.uri) { + result.errors.emplace_back("GLB has a binary chunk but the first buffer in the JSON chunk also has a 'uri'."); + return result; + } + + int64_t binaryChunkSize = static_cast(binaryChunk.size()); + if (buffer.byteLength > binaryChunkSize || buffer.byteLength + 3 < binaryChunkSize) { + result.errors.emplace_back("GLB binary chunk size does not match the size of the first buffer in the JSON chunk."); + return result; + } + + buffer.cesium.data = std::vector(binaryChunk.begin(), binaryChunk.begin() + buffer.byteLength); + } + + return result; + } + + void postprocess(ModelReaderResult& readModel, const ReadModelOptions& options) { + Model& model = readModel.model.value(); + + if (options.decodeDataUrls) { + decodeDataUrls(readModel, options.clearDecodedDataUrls); + } + + if (options.decodeEmbeddedImages) { + for (Image& image : model.images) { + const BufferView& bufferView = Model::getSafe(model.bufferViews, image.bufferView); + const Buffer& buffer = Model::getSafe(model.buffers, bufferView.buffer); + + if (bufferView.byteOffset + bufferView.byteLength > static_cast(buffer.cesium.data.size())) { + readModel.warnings.emplace_back("Image bufferView's byteLength is more than the available bytes."); + continue; + } + + gsl::span bufferSpan(buffer.cesium.data); + gsl::span bufferViewSpan = bufferSpan.subspan(static_cast(bufferView.byteOffset), static_cast(bufferView.byteLength)); + ImageReaderResult imageResult = readImage(bufferViewSpan); + if (imageResult.image) { + image.cesium = std::move(imageResult.image.value()); + } + } + } + + if (options.decodeDraco) { + decodeDraco(readModel); + } + } +} + +ModelReaderResult CesiumGltf::readModel(const gsl::span& data, const ReadModelOptions& options) { + ModelReaderResult result = isBinaryGltf(data) ? readBinaryModel(data) : readJsonModel(data); + + if (result.model) { + postprocess(result, options); + } + + return result; +} + +ImageReaderResult CesiumGltf::readImage(const gsl::span& data) { + ImageReaderResult result; + + result.image.emplace(); + ImageCesium& image = result.image.value(); + + image.bytesPerChannel = 1; + image.channels = 4; + + int channelsInFile; + stbi_uc* pImage = stbi_load_from_memory(data.data(), static_cast(data.size()), &image.width, &image.height, &channelsInFile, image.channels); + if (pImage) { + image.pixelData.assign(pImage, pImage + image.width * image.height * image.channels * image.bytesPerChannel); + stbi_image_free(pImage); + } else { + result.image.reset(); + result.errors.emplace_back(stbi_failure_reason()); + } + + return result; +} \ No newline at end of file diff --git a/CesiumGltfReader/src/StringJsonHandler.cpp b/CesiumGltfReader/src/StringJsonHandler.cpp new file mode 100644 index 000000000..42bb95b14 --- /dev/null +++ b/CesiumGltfReader/src/StringJsonHandler.cpp @@ -0,0 +1,17 @@ +#include "StringJsonHandler.h" + +using namespace CesiumGltf; + +void StringJsonHandler::reset(IJsonHandler* pParent, std::string* pString) { + JsonHandler::reset(pParent); + this->_pString = pString; +} + +std::string* StringJsonHandler::getObject() { + return this->_pString; +} + +IJsonHandler* StringJsonHandler::String(const char* str, size_t length, bool /*copy*/) { + *this->_pString = std::string(str, length); + return this->parent(); +} diff --git a/CesiumGltfReader/src/StringJsonHandler.h b/CesiumGltfReader/src/StringJsonHandler.h new file mode 100644 index 000000000..cd05b5816 --- /dev/null +++ b/CesiumGltfReader/src/StringJsonHandler.h @@ -0,0 +1,16 @@ +#pragma once + +#include "JsonHandler.h" +#include + +namespace CesiumGltf { + class StringJsonHandler : public JsonHandler { + public: + void reset(IJsonHandler* pParent, std::string* pString); + std::string* getObject(); + virtual IJsonHandler* String(const char* str, size_t length, bool copy) override; + + private: + std::string* _pString = nullptr; + }; +} diff --git a/CesiumGltfReader/src/decodeDataUrls.cpp b/CesiumGltfReader/src/decodeDataUrls.cpp new file mode 100644 index 000000000..d5276e3a5 --- /dev/null +++ b/CesiumGltfReader/src/decodeDataUrls.cpp @@ -0,0 +1,119 @@ +#include "CesiumGltf/Model.h" +#include "CesiumGltf/Reader.h" +#include "decodeDataUrls.h" +#include "modp_b64.h" + +namespace { + + std::vector decodeBase64(gsl::span data) { + std::vector result(modp_b64_decode_len(data.size())); + + size_t resultLength = modp_b64_decode(reinterpret_cast(result.data()), reinterpret_cast(data.data()), data.size()); + if (resultLength == size_t(-1)) { + result.clear(); + result.shrink_to_fit(); + } else { + result.resize(resultLength); + } + + return result; + } + + struct DecodeResult { + std::string mimeType; + std::vector data; + }; + + std::optional tryDecode(const std::string& uri) { + constexpr std::string_view dataPrefix = "data:"; + constexpr size_t dataPrefixLength = dataPrefix.size(); + + constexpr std::string_view base64Indicator = ";base64"; + constexpr size_t base64IndicatorLength = base64Indicator.size(); + + if (uri.substr(0, dataPrefixLength) != dataPrefix) { + return std::nullopt; + } + + size_t dataDelimeter = uri.find(',', dataPrefixLength); + if (dataDelimeter == std::string::npos) { + return std::nullopt; + } + + bool isBase64Encoded = false; + + DecodeResult result; + + result.mimeType = uri.substr(dataPrefixLength, dataDelimeter - dataPrefixLength); + if ( + result.mimeType.size() >= base64IndicatorLength && + result.mimeType.substr(result.mimeType.size() - base64IndicatorLength, base64IndicatorLength) == base64Indicator + ) { + isBase64Encoded = true; + result.mimeType = result.mimeType.substr(0, result.mimeType.size() - base64IndicatorLength); + } + + gsl::span data(reinterpret_cast(uri.data()) + dataDelimeter + 1, uri.size() - dataDelimeter - 1); + + if (isBase64Encoded) { + result.data = decodeBase64(data); + if (result.data.empty() && !data.empty()) { + // base64 decode failed. + return std::nullopt; + } + } else { + result.data = std::vector(data.begin(), data.end()); + } + + return result; + } +} + +namespace CesiumGltf { + +void decodeDataUrls(ModelReaderResult& readModel, bool clearDecodedDataUrls) { + if (!readModel.model) { + return; + } + + Model& model = readModel.model.value(); + + for (Buffer& buffer : model.buffers) { + if (!buffer.uri) { + continue; + } + + std::optional decoded = tryDecode(buffer.uri.value()); + if (!decoded) { + continue; + } + + buffer.cesium.data = std::move(decoded.value().data); + + if (clearDecodedDataUrls) { + buffer.uri.reset(); + } + } + + for (Image& image : model.images) { + if (!image.uri) { + continue; + } + + std::optional decoded = tryDecode(image.uri.value()); + if (!decoded) { + continue; + } + + ImageReaderResult imageResult = readImage(decoded.value().data); + if (imageResult.image) { + image.cesium = std::move(imageResult.image.value()); + } + + if (clearDecodedDataUrls) { + image.uri.reset(); + } + } +} + +} diff --git a/CesiumGltfReader/src/decodeDataUrls.h b/CesiumGltfReader/src/decodeDataUrls.h new file mode 100644 index 000000000..0dc62b6f4 --- /dev/null +++ b/CesiumGltfReader/src/decodeDataUrls.h @@ -0,0 +1,7 @@ +#pragma once + +namespace CesiumGltf { + struct ModelReaderResult; + + void decodeDataUrls(ModelReaderResult& readModel, bool clearDecodedDataUrls); +} diff --git a/CesiumGltfReader/src/decodeDraco.cpp b/CesiumGltfReader/src/decodeDraco.cpp new file mode 100644 index 000000000..b5dda07e7 --- /dev/null +++ b/CesiumGltfReader/src/decodeDraco.cpp @@ -0,0 +1,270 @@ +#include "decodeDraco.h" +#include "CesiumGltf/Model.h" +#include "CesiumGltf/KHR_draco_mesh_compression.h" +#include "CesiumGltf/Reader.h" +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4127) +#endif + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +namespace { + using namespace CesiumGltf; + + std::unique_ptr decodeBufferViewToDracoMesh( + ModelReaderResult& readModel, + MeshPrimitive& /* primitive */, + KHR_draco_mesh_compression& draco + ) { + Model& model = readModel.model.value(); + + BufferView* pBufferView = Model::getSafe(&model.bufferViews, draco.bufferView); + if (!pBufferView) { + readModel.warnings.emplace_back("Draco bufferView index is invalid."); + return nullptr; + } + + BufferView& bufferView = *pBufferView; + + Buffer* pBuffer = Model::getSafe(&model.buffers, bufferView.buffer); + if (!pBuffer) { + readModel.warnings.emplace_back("Draco bufferView has an invalid buffer index."); + return nullptr; + } + + Buffer& buffer = *pBuffer; + + if (bufferView.byteOffset < 0 || bufferView.byteLength < 0 || bufferView.byteOffset + bufferView.byteLength > static_cast(buffer.cesium.data.size())) { + readModel.warnings.emplace_back("Draco bufferView extends beyond its buffer."); + return nullptr; + } + + gsl::span data(buffer.cesium.data.data() + bufferView.byteOffset, static_cast(bufferView.byteLength)); + + draco::DecoderBuffer decodeBuffer; + decodeBuffer.Init(reinterpret_cast(data.data()), data.size()); + + draco::Decoder decoder; + draco::Mesh mesh; + draco::StatusOr> result = decoder.DecodeMeshFromBuffer(&decodeBuffer); + if (!result.ok()) { + readModel.warnings.emplace_back(std::string("Draco decoding failed: ") + result.status().error_msg_string()); + return nullptr; + } + + return std::move(result).value(); + } + + template + void copyData(const TSource* pSource, TDestination* pDestination, int64_t length) { + std::transform(pSource, pSource + length, pDestination, [](auto x) { return static_cast(x); }); + } + + template + void copyData(const T* pSource, T* pDestination, int64_t length) { + std::copy(pSource, pSource + length, pDestination); + } + + void copyDecodedIndices( + ModelReaderResult& readModel, + MeshPrimitive& primitive, + draco::Mesh* pMesh + ) { + Model& model = readModel.model.value(); + + if (primitive.indices < 0) { + return; + } + + Accessor* pIndicesAccessor = Model::getSafe(&model.accessors, primitive.indices); + if (!pIndicesAccessor) { + readModel.warnings.emplace_back("Primitive indices accessor ID is invalid."); + return; + } + + if (pIndicesAccessor->count > pMesh->num_faces() * 3) { + readModel.warnings.emplace_back("There are fewer decoded Draco indices than are expected by the accessor."); + + pIndicesAccessor->count = pMesh->num_faces() * 3; + } + + pIndicesAccessor->bufferView = static_cast(model.bufferViews.size()); + BufferView& indicesBufferView = model.bufferViews.emplace_back(); + + indicesBufferView.buffer = static_cast(model.buffers.size()); + Buffer& indicesBuffer = model.buffers.emplace_back(); + + int64_t indexBytes = pIndicesAccessor->computeByteSizeOfComponent(); + int64_t indicesBytes = pIndicesAccessor->count * indexBytes; + + indicesBuffer.cesium.data.resize(static_cast(indicesBytes)); + indicesBuffer.byteLength = indicesBytes; + indicesBufferView.byteLength = indicesBytes; + indicesBufferView.byteStride = indexBytes; + indicesBufferView.byteOffset = 0; + indicesBufferView.target = BufferView::Target::ELEMENT_ARRAY_BUFFER; + pIndicesAccessor->type = Accessor::Type::SCALAR; + + static_assert(sizeof(draco::PointIndex) == sizeof(uint32_t)); + + const uint32_t* pSourceIndices = reinterpret_cast(&pMesh->face(draco::FaceIndex(0))[0]); + + switch (pIndicesAccessor->componentType) { + case Accessor::ComponentType::BYTE: + copyData(pSourceIndices, reinterpret_cast(indicesBuffer.cesium.data.data()), pIndicesAccessor->count); + break; + case Accessor::ComponentType::UNSIGNED_BYTE: + copyData(pSourceIndices, reinterpret_cast(indicesBuffer.cesium.data.data()), pIndicesAccessor->count); + break; + case Accessor::ComponentType::SHORT: + copyData(pSourceIndices, reinterpret_cast(indicesBuffer.cesium.data.data()), pIndicesAccessor->count); + break; + case Accessor::ComponentType::UNSIGNED_SHORT: + copyData(pSourceIndices, reinterpret_cast(indicesBuffer.cesium.data.data()), pIndicesAccessor->count); + break; + case Accessor::ComponentType::UNSIGNED_INT: + copyData(pSourceIndices, reinterpret_cast(indicesBuffer.cesium.data.data()), pIndicesAccessor->count); + break; + case Accessor::ComponentType::FLOAT: + copyData(pSourceIndices, reinterpret_cast(indicesBuffer.cesium.data.data()), pIndicesAccessor->count); + break; + } + } + + void copyDecodedAttribute( + ModelReaderResult& readModel, + MeshPrimitive& /* primitive */, + Accessor* pAccessor, + const draco::Mesh* pMesh, + const draco::PointAttribute* pAttribute + ) { + Model& model = readModel.model.value(); + + if (pAccessor->count > pMesh->num_points()) { + readModel.warnings.emplace_back("There are fewer decoded Draco indices than are expected by the accessor."); + + pAccessor->count = pMesh->num_points(); + } + + pAccessor->bufferView = static_cast(model.bufferViews.size()); + BufferView& bufferView = model.bufferViews.emplace_back(); + + bufferView.buffer = static_cast(model.buffers.size()); + Buffer& buffer = model.buffers.emplace_back(); + + int8_t numberOfComponents = pAccessor->computeNumberOfComponents(); + int64_t stride = numberOfComponents * pAccessor->computeByteSizeOfComponent(); + int64_t sizeBytes = pAccessor->count * stride; + + buffer.cesium.data.resize(static_cast(sizeBytes)); + buffer.byteLength = sizeBytes; + bufferView.byteLength = sizeBytes; + bufferView.byteStride = stride; + bufferView.byteOffset = 0; + pAccessor->byteOffset = 0; + + auto doCopy = [pMesh, pAttribute, numberOfComponents](auto pOut) { + for (draco::PointIndex i(0); i < pMesh->num_points(); ++i) { + draco::AttributeValueIndex valueIndex = pAttribute->mapped_index(i); + pAttribute->ConvertValue(valueIndex, numberOfComponents, pOut); + pOut += pAttribute->num_components(); + } + }; + + switch (pAccessor->componentType) { + case Accessor::ComponentType::BYTE: + doCopy(reinterpret_cast(buffer.cesium.data.data())); + break; + case Accessor::ComponentType::UNSIGNED_BYTE: + doCopy(reinterpret_cast(buffer.cesium.data.data())); + break; + case Accessor::ComponentType::SHORT: + doCopy(reinterpret_cast(buffer.cesium.data.data())); + break; + case Accessor::ComponentType::UNSIGNED_SHORT: + doCopy(reinterpret_cast(buffer.cesium.data.data())); + break; + case Accessor::ComponentType::UNSIGNED_INT: + doCopy(reinterpret_cast(buffer.cesium.data.data())); + break; + case Accessor::ComponentType::FLOAT: + doCopy(reinterpret_cast(buffer.cesium.data.data())); + break; + default: + readModel.warnings.emplace_back("Accessor uses an unknown componentType: " + std::to_string(int32_t(pAccessor->componentType))); + break; + } + } + + void decodePrimitive( + ModelReaderResult& readModel, + MeshPrimitive& primitive, + KHR_draco_mesh_compression& draco + ) { + Model& model = readModel.model.value(); + + std::unique_ptr pMesh = decodeBufferViewToDracoMesh(readModel, primitive, draco); + if (!pMesh) { + return; + } + + copyDecodedIndices(readModel, primitive, pMesh.get()); + + for (const std::pair& attribute : draco.attributes) { + auto primitiveAttrIt = primitive.attributes.find(attribute.first); + if (primitiveAttrIt == primitive.attributes.end()) { + // The primitive does not use this attribute. The KHR_draco_mesh_compression spec + // says this shouldn't happen, so warn. + readModel.warnings.emplace_back("Draco extension has the " + attribute.first + " attribute, but the primitive does not have that attribute."); + continue; + } + + int32_t primitiveAttrIndex = primitiveAttrIt->second; + Accessor* pAccessor = Model::getSafe(&model.accessors, primitiveAttrIndex); + if (!pAccessor) { + readModel.warnings.emplace_back("Primitive attribute's accessor index is invalid."); + continue; + } + + int32_t dracoAttrIndex = attribute.second; + const draco::PointAttribute* pAttribute = pMesh->GetAttributeByUniqueId(static_cast(dracoAttrIndex)); + if (pAttribute == nullptr) { + readModel.warnings.emplace_back("Draco attribute with unique ID " + std::to_string(dracoAttrIndex) + " does not exist."); + continue; + } + + copyDecodedAttribute(readModel, primitive, pAccessor, pMesh.get(), pAttribute); + } + } +} + +namespace CesiumGltf { + +void decodeDraco(CesiumGltf::ModelReaderResult& readModel) { + if (!readModel.model) { + return; + } + + Model& model = readModel.model.value(); + + for (Mesh& mesh : model.meshes) { + for (MeshPrimitive& primitive : mesh.primitives) { + KHR_draco_mesh_compression* pDraco = primitive.getExtension(); + if (!pDraco) { + continue; + } + + decodePrimitive(readModel, primitive, *pDraco); + } + } +} + +} \ No newline at end of file diff --git a/CesiumGltfReader/src/decodeDraco.h b/CesiumGltfReader/src/decodeDraco.h new file mode 100644 index 000000000..d925e1939 --- /dev/null +++ b/CesiumGltfReader/src/decodeDraco.h @@ -0,0 +1,7 @@ +#pragma once + +namespace CesiumGltf { + struct ModelReaderResult; + + void decodeDraco(ModelReaderResult& readModel); +} diff --git a/CesiumGltfReader/test/TestReader.cpp b/CesiumGltfReader/test/TestReader.cpp new file mode 100644 index 000000000..1aefc6494 --- /dev/null +++ b/CesiumGltfReader/test/TestReader.cpp @@ -0,0 +1,108 @@ +#include "catch2/catch.hpp" +#include "CesiumGltf/Reader.h" +#include "CesiumGltf/AccessorView.h" +#include +#include +#include +#include +#include + +using namespace CesiumGltf; + +namespace { + std::vector readFile(const std::string& path) { + FILE* fp = std::fopen(path.c_str(), "rb"); + REQUIRE(fp); + + try { + std::fseek(fp, 0, SEEK_END); + long pos = std::ftell(fp); + std::fseek(fp, 0, SEEK_SET); + + std::vector result(static_cast(pos)); + size_t itemsRead = std::fread(result.data(), 1, result.size(), fp); + REQUIRE(itemsRead == result.size()); + + std::fclose(fp); + + return result; + } catch(...) { + if (fp) { + std::fclose(fp); + } + throw; + } + } +} + +TEST_CASE("CesiumGltf::Reader") { + using namespace std::string_literals; + + std::string s = + "{"s + + " \"accessors\": ["s + + " {"s + + " \"count\": 4,"s + //{\"test\":true},"s + + " \"componentType\":5121,"s + + " \"type\":\"VEC2\","s + + " \"max\":[1.0, 2.2, 3.3],"s + + " \"min\":[0.0, -1.2]"s + + " }"s + + " ],"s + + " \"meshes\": [{"s + + " \"primitives\": [{"s + + " \"attributes\": {"s + + " \"POSITION\": 0,"s + + " \"NORMAL\": 1"s + + " },"s + + " \"targets\": ["s + + " {\"POSITION\": 10, \"NORMAL\": 11}"s + + " ]"s + + " }]"s + + " }],"s + + " \"surprise\":{\"foo\":true}"s + + "}"s; + ModelReaderResult result = CesiumGltf::readModel(gsl::span(reinterpret_cast(s.c_str()), s.size())); + CHECK(result.errors.empty()); + REQUIRE(result.model.has_value()); + + Model& model = result.model.value(); + REQUIRE(model.accessors.size() == 1); + CHECK(model.accessors[0].count == 4); + CHECK(model.accessors[0].componentType == Accessor::ComponentType::UNSIGNED_BYTE); + CHECK(model.accessors[0].type == Accessor::Type::VEC2); + REQUIRE(model.accessors[0].min.size() == 2); + CHECK(model.accessors[0].min[0] == 0.0); + CHECK(model.accessors[0].min[1] == -1.2); + REQUIRE(model.accessors[0].max.size() == 3); + CHECK(model.accessors[0].max[0] == 1.0); + CHECK(model.accessors[0].max[1] == 2.2); + CHECK(model.accessors[0].max[2] == 3.3); + + REQUIRE(model.meshes.size() == 1); + REQUIRE(model.meshes[0].primitives.size() == 1); + CHECK(model.meshes[0].primitives[0].attributes["POSITION"] == 0); + CHECK(model.meshes[0].primitives[0].attributes["NORMAL"] == 1); + + REQUIRE(model.meshes[0].primitives[0].targets.size() == 1); + CHECK(model.meshes[0].primitives[0].targets[0]["POSITION"] == 10); + CHECK(model.meshes[0].primitives[0].targets[0]["NORMAL"] == 11); +} + +TEST_CASE("Read TriangleWithoutIndices") { + std::vector data = readFile("../CesiumGltfReader/test/data/TriangleWithoutIndices.gltf"); + ModelReaderResult result = CesiumGltf::readModel(data); + REQUIRE(result.model); + + const Model& model = result.model.value(); + REQUIRE(model.meshes.size() == 1); + REQUIRE(model.meshes[0].primitives.size() == 1); + REQUIRE(model.meshes[0].primitives[0].attributes.size() == 1); + REQUIRE(model.meshes[0].primitives[0].attributes.begin()->second == 0); + + AccessorView position(model, 0); + REQUIRE(position.size() == 3); + CHECK(position[0] == glm::vec3(0.0, 0.0, 0.0)); + CHECK(position[1] == glm::vec3(1.0, 0.0, 0.0)); + CHECK(position[2] == glm::vec3(0.0, 1.0, 0.0)); +} diff --git a/CesiumGltfReader/test/data/README.md b/CesiumGltfReader/test/data/README.md new file mode 100644 index 000000000..472fc2be9 --- /dev/null +++ b/CesiumGltfReader/test/data/README.md @@ -0,0 +1,2 @@ +Test models from: +https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0 diff --git a/CesiumGltfReader/test/data/TriangleWithoutIndices.gltf b/CesiumGltfReader/test/data/TriangleWithoutIndices.gltf new file mode 100644 index 000000000..1c0736ab3 --- /dev/null +++ b/CesiumGltfReader/test/data/TriangleWithoutIndices.gltf @@ -0,0 +1,54 @@ +{ + "scene" : 0, + "scenes" : [ + { + "nodes" : [ 0 ] + } + ], + + "nodes" : [ + { + "mesh" : 0 + } + ], + + "meshes" : [ + { + "primitives" : [ { + "attributes" : { + "POSITION" : 0 + } + } ] + } + ], + + "buffers" : [ + { + "uri" : "data:application/octet-stream;base64,AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAA", + "byteLength" : 36 + } + ], + "bufferViews" : [ + { + "buffer" : 0, + "byteOffset" : 0, + "byteLength" : 36, + "target" : 34962 + } + ], + "accessors" : [ + { + "bufferView" : 0, + "byteOffset" : 0, + "componentType" : 5126, + "count" : 3, + "type" : "VEC3", + "max" : [ 1.0, 1.0, 0.0 ], + "min" : [ 0.0, 0.0, 0.0 ] + } + ], + + "asset" : { + "version" : "2.0" + } +} \ No newline at end of file diff --git a/CesiumUtility/include/CesiumUtility/joinToString.h b/CesiumUtility/include/CesiumUtility/joinToString.h new file mode 100644 index 000000000..36e1fc968 --- /dev/null +++ b/CesiumUtility/include/CesiumUtility/joinToString.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + +namespace CesiumUtility { + /** + * @brief Joins multiple elements together into a string, separated by a given separator. + * + * @tparam TIterator The type of the collection iterator. + * @param begin An iterator referring to the first element to join. + * @param end An iterator referring to one past the last element to join. + * @param separator The string to use to separate successive elements. + * @return The joined string. + */ + template + std::string joinToString(TIterator begin, TIterator end, const std::string& separator) { + return std::accumulate(begin, end, std::string(), [&separator](const std::string& acc, const std::string& element) { + if (!acc.empty()) { + return acc + separator + element; + } else { + return element; + } + }); + } + + /** + * @brief Joins multiple elements together into a string, separated by a given separator. + * + * @tparam TIterator The type of the collection iterator. + * @param collection The collection of elements to be joined. + * @param separator The string to use to separate successive elements. + * @return The joined string. + */ + template + std::string joinToString(TCollection collection, const std::string& separator) { + return std::accumulate(collection.cbegin(), collection.cend(), std::string(), [&separator](const std::string& acc, const std::string& element) { + if (!acc.empty()) { + return acc + separator + element; + } else { + return element; + } + }); + } +} \ No newline at end of file diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 1112d9141..ce259489e 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -4,8 +4,11 @@ if (DOXYGEN_FOUND) set( LIB_DIRS ../Cesium3DTiles/include - ../CesiumGeospatial/include + ../CesiumAsync/include ../CesiumGeometry/include + ../CesiumGeospatial/include + ../CesiumGltf/include + ../CesiumGltfReader/include ../CesiumUtility/include ) @@ -13,8 +16,11 @@ if (DOXYGEN_FOUND) set( DOXYGEN_EXAMPLE_PATH ../Cesium3DTiles/test - ../CesiumGeospatial/test + ../CesiumAsync/test ../CesiumGeometry/test + ../CesiumGeospatial/test + ../CesiumGltf/test + ../CesiumGltfReader/test ../CesiumUtility/test ) diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index c6af6a0e8..0f3234a76 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -18,3 +18,5 @@ add_subdirectory(tinyxml2) add_subdirectory(asyncplusplus) add_subdirectory(spdlog) + +add_subdirectory(modp_b64) \ No newline at end of file diff --git a/extern/glTF b/extern/glTF new file mode 160000 index 000000000..b238d8405 --- /dev/null +++ b/extern/glTF @@ -0,0 +1 @@ +Subproject commit b238d84053ef3090f9818f8a4ae5d7f0f90dbe4d diff --git a/extern/modp_b64/CMakeLists.txt b/extern/modp_b64/CMakeLists.txt new file mode 100644 index 000000000..db8192ab4 --- /dev/null +++ b/extern/modp_b64/CMakeLists.txt @@ -0,0 +1,7 @@ +add_library(modp_b64 STATIC modp_b64.cc modp_b64.h modp_b64_data.h) + +target_include_directories( + modp_b64 + SYSTEM PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} +) diff --git a/extern/modp_b64/LICENSE b/extern/modp_b64/LICENSE new file mode 100644 index 000000000..55af76f3e --- /dev/null +++ b/extern/modp_b64/LICENSE @@ -0,0 +1,33 @@ + * MODP_B64 - High performance base64 encoder/decoder + * Version 1.3 -- 17-Mar-2006 + * http://modp.com/release/base64 + * + * Copyright (c) 2005, 2006 Nick Galbreath -- nickg [at] modp [dot] com + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of the modp.com nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/extern/modp_b64/README.cesium b/extern/modp_b64/README.cesium new file mode 100644 index 000000000..86b62e2c0 --- /dev/null +++ b/extern/modp_b64/README.cesium @@ -0,0 +1,6 @@ +This is a fork of Chromium's modp_b64 at commit 15996b5d2322b634f4197447b10289bddc2b0b32: +https://github.com/chromium/chromium/tree/15996b5d2322b634f4197447b10289bddc2b0b32/third_party/modp_b64 + +It is unmodified from the original at the initial commit to Cesium. We have only added this README and a CMakeLists.txt. + +Chromium's implementation is, in turn, a fork of https://github.com/client9/stringencoders. diff --git a/extern/modp_b64/README.chromium b/extern/modp_b64/README.chromium new file mode 100644 index 000000000..364a05073 --- /dev/null +++ b/extern/modp_b64/README.chromium @@ -0,0 +1,17 @@ +Name: modp base64 decoder +Short Name: stringencoders +URL: https://github.com/client9/stringencoders +Version: unknown +License: BSD +Security Critical: yes + +Description: +The modp_b64.c file was modified to remove the inclusion of modp's config.h +and to fix compilation errors that occur under VC8. The file was renamed +modp_b64.cc to force it to be compiled as C++ so that the inclusion of +basictypes.h could be possible. + +The modp_b64.cc and modp_b64.h files were modified to make them safe on +64-bit systems. +The modp_b64.cc was modified to avoid misaligned read/write on +little-endian hardware. diff --git a/extern/modp_b64/modp_b64.cc b/extern/modp_b64/modp_b64.cc new file mode 100644 index 000000000..fdb8a40ec --- /dev/null +++ b/extern/modp_b64/modp_b64.cc @@ -0,0 +1,253 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set expandtab shiftwidth=4 tabstop=4: */ +/** + * \file + *
+ * MODP_B64 - High performance base64 encoder/decoder
+ * Version 1.3 -- 17-Mar-2006
+ * http://modp.com/release/base64
+ *
+ * Copyright © 2005, 2006  Nick Galbreath -- nickg [at] modp [dot] com
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *   Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ *   Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ *   Neither the name of the modp.com nor the names of its
+ *   contributors may be used to endorse or promote products derived from
+ *   this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This is the standard "new" BSD license:
+ * http://www.opensource.org/licenses/bsd-license.php
+ * 
+ */ + +/* public header */ +#include "modp_b64.h" + +/* + * If you are ripping this out of the library, comment out the next + * line and uncomment the next lines as approrpiate + */ +//#include "config.h" + +/* if on motoral, sun, ibm; uncomment this */ +/* #define WORDS_BIGENDIAN 1 */ +/* else for Intel, Amd; uncomment this */ +/* #undef WORDS_BIGENDIAN */ + +#include "modp_b64_data.h" + +#define BADCHAR 0x01FFFFFF + +/** + * you can control if we use padding by commenting out this + * next line. However, I highly recommend you use padding and not + * using it should only be for compatability with a 3rd party. + * Also, 'no padding' is not tested! + */ +#define DOPAD 1 + +/* + * if we aren't doing padding + * set the pad character to NULL + */ +#ifndef DOPAD +#undef CHARPAD +#define CHARPAD '\0' +#endif + +size_t modp_b64_encode(char* dest, const char* str, size_t len) +{ + size_t i = 0; + uint8_t* p = (uint8_t*) dest; + + /* unsigned here is important! */ + uint8_t t1, t2, t3; + + if (len > 2) { + for (; i < len - 2; i += 3) { + t1 = str[i]; t2 = str[i+1]; t3 = str[i+2]; + *p++ = e0[t1]; + *p++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; + *p++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)]; + *p++ = e2[t3]; + } + } + + switch (len - i) { + case 0: + break; + case 1: + t1 = str[i]; + *p++ = e0[t1]; + *p++ = e1[(t1 & 0x03) << 4]; + *p++ = CHARPAD; + *p++ = CHARPAD; + break; + default: /* case 2 */ + t1 = str[i]; t2 = str[i+1]; + *p++ = e0[t1]; + *p++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; + *p++ = e2[(t2 & 0x0F) << 2]; + *p++ = CHARPAD; + } + + *p = '\0'; + return p - (uint8_t*)dest; +} + +#ifdef WORDS_BIGENDIAN /* BIG ENDIAN -- SUN / IBM / MOTOROLA */ +int modp_b64_decode(char* dest, const char* src, int len) +{ + if (len == 0) return 0; + +#ifdef DOPAD + /* if padding is used, then the message must be at least + 4 chars and be a multiple of 4. + there can be at most 2 pad chars at the end */ + if (len < 4 || (len % 4 != 0)) return MODP_B64_ERROR; + if (src[len-1] == CHARPAD) { + len--; + if (src[len -1] == CHARPAD) { + len--; + } + } +#endif /* DOPAD */ + + size_t i; + int leftover = len % 4; + size_t chunks = (leftover == 0) ? len / 4 - 1 : len /4; + + uint8_t* p = (uint8_t*) dest; + uint32_t x = 0; + uint32_t* destInt = (uint32_t*) p; + uint32_t* srcInt = (uint32_t*) src; + uint32_t y = *srcInt++; + for (i = 0; i < chunks; ++i) { + x = d0[y >> 24 & 0xff] | d1[y >> 16 & 0xff] | + d2[y >> 8 & 0xff] | d3[y & 0xff]; + + if (x >= BADCHAR) return MODP_B64_ERROR; + *destInt = x << 8; + p += 3; + destInt = (uint32_t*)p; + y = *srcInt++; + } + + switch (leftover) { + case 0: + x = d0[y >> 24 & 0xff] | d1[y >> 16 & 0xff] | + d2[y >> 8 & 0xff] | d3[y & 0xff]; + if (x >= BADCHAR) return MODP_B64_ERROR; + *p++ = ((uint8_t*)&x)[1]; + *p++ = ((uint8_t*)&x)[2]; + *p = ((uint8_t*)&x)[3]; + return (chunks+1)*3; + case 1: + x = d3[y >> 24]; + *p = (uint8_t)x; + break; + case 2: + x = d3[y >> 24] *64 + d3[(y >> 16) & 0xff]; + *p = (uint8_t)(x >> 4); + break; + default: /* case 3 */ + x = (d3[y >> 24] *64 + d3[(y >> 16) & 0xff])*64 + + d3[(y >> 8) & 0xff]; + *p++ = (uint8_t) (x >> 10); + *p = (uint8_t) (x >> 2); + break; + } + + if (x >= BADCHAR) return MODP_B64_ERROR; + return 3*chunks + (6*leftover)/8; +} + +#else /* LITTLE ENDIAN -- INTEL AND FRIENDS */ + +size_t modp_b64_decode(char* dest, const char* src, size_t len) +{ + if (len == 0) return 0; + +#ifdef DOPAD + /* + * if padding is used, then the message must be at least + * 4 chars and be a multiple of 4 + */ + if (len < 4 || (len % 4 != 0)) return MODP_B64_ERROR; /* error */ + /* there can be at most 2 pad chars at the end */ + if (src[len-1] == CHARPAD) { + len--; + if (src[len -1] == CHARPAD) { + len--; + } + } +#endif + + size_t i; + int leftover = len % 4; + size_t chunks = (leftover == 0) ? len / 4 - 1 : len /4; + + uint8_t* p = (uint8_t*)dest; + uint32_t x = 0; + const uint8_t* y = (uint8_t*)src; + for (i = 0; i < chunks; ++i, y += 4) { + x = d0[y[0]] | d1[y[1]] | d2[y[2]] | d3[y[3]]; + if (x >= BADCHAR) return MODP_B64_ERROR; + *p++ = ((uint8_t*)(&x))[0]; + *p++ = ((uint8_t*)(&x))[1]; + *p++ = ((uint8_t*)(&x))[2]; + } + + switch (leftover) { + case 0: + x = d0[y[0]] | d1[y[1]] | d2[y[2]] | d3[y[3]]; + + if (x >= BADCHAR) return MODP_B64_ERROR; + *p++ = ((uint8_t*)(&x))[0]; + *p++ = ((uint8_t*)(&x))[1]; + *p = ((uint8_t*)(&x))[2]; + return (chunks+1)*3; + break; + case 1: /* with padding this is an impossible case */ + x = d0[y[0]]; + *p = *((uint8_t*)(&x)); // i.e. first char/byte in int + break; + case 2: // * case 2, 1 output byte */ + x = d0[y[0]] | d1[y[1]]; + *p = *((uint8_t*)(&x)); // i.e. first char + break; + default: /* case 3, 2 output bytes */ + x = d0[y[0]] | d1[y[1]] | d2[y[2]]; /* 0x3c */ + *p++ = ((uint8_t*)(&x))[0]; + *p = ((uint8_t*)(&x))[1]; + break; + } + + if (x >= BADCHAR) return MODP_B64_ERROR; + + return 3*chunks + (6*leftover)/8; +} + +#endif /* if bigendian / else / endif */ diff --git a/extern/modp_b64/modp_b64.h b/extern/modp_b64/modp_b64.h new file mode 100644 index 000000000..3270e5fdf --- /dev/null +++ b/extern/modp_b64/modp_b64.h @@ -0,0 +1,171 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set expandtab shiftwidth=4 tabstop=4: */ + +/** + * \file + *
+ * High performance base64 encoder / decoder
+ * Version 1.3 -- 17-Mar-2006
+ *
+ * Copyright © 2005, 2006, Nick Galbreath -- nickg [at] modp [dot] com
+ * All rights reserved.
+ *
+ * http://modp.com/release/base64
+ *
+ * Released under bsd license.  See modp_b64.c for details.
+ * 
+ * + * The default implementation is the standard b64 encoding with padding. + * It's easy to change this to use "URL safe" characters and to remove + * padding. See the modp_b64.c source code for details. + * + */ + +#ifndef MODP_B64 +#define MODP_B64 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Encode a raw binary string into base 64. + * src contains the bytes + * len contains the number of bytes in the src + * dest should be allocated by the caller to contain + * at least modp_b64_encode_len(len) bytes (see below) + * This will contain the null-terminated b64 encoded result + * returns length of the destination string plus the ending null byte + * i.e. the result will be equal to strlen(dest) + 1 + * + * Example + * + * \code + * char* src = ...; + * int srclen = ...; //the length of number of bytes in src + * char* dest = (char*) malloc(modp_b64_encode_len); + * int len = modp_b64_encode(dest, src, sourcelen); + * if (len == -1) { + * printf("Error\n"); + * } else { + * printf("b64 = %s\n", dest); + * } + * \endcode + * + */ +size_t modp_b64_encode(char* dest, const char* str, size_t len); + +/** + * Decode a base64 encoded string + * + * src should contain exactly len bytes of b64 characters. + * if src contains -any- non-base characters (such as white + * space, -1 is returned. + * + * dest should be allocated by the caller to contain at least + * len * 3 / 4 bytes. + * + * Returns the length (strlen) of the output, or -1 if unable to + * decode + * + * \code + * char* src = ...; + * int srclen = ...; // or if you don't know use strlen(src) + * char* dest = (char*) malloc(modp_b64_decode_len(srclen)); + * int len = modp_b64_decode(dest, src, sourcelen); + * if (len == -1) { error } + * \endcode + */ +size_t modp_b64_decode(char* dest, const char* src, size_t len); + +/** + * Given a source string of length len, this returns the amount of + * memory the destination string should have. + * + * remember, this is integer math + * 3 bytes turn into 4 chars + * ceiling[len / 3] * 4 + 1 + * + * +1 is for any extra null. + */ +#define modp_b64_encode_len(A) ((A+2)/3 * 4 + 1) + +/** + * Given a base64 string of length len, + * this returns the amount of memory required for output string + * It maybe be more than the actual number of bytes written. + * NOTE: remember this is integer math + * this allocates a bit more memory than traditional versions of b64 + * decode 4 chars turn into 3 bytes + * floor[len * 3/4] + 2 + */ +#define modp_b64_decode_len(A) (A / 4 * 3 + 2) + +/** + * Will return the strlen of the output from encoding. + * This may be less than the required number of bytes allocated. + * + * This allows you to 'deserialized' a struct + * \code + * char* b64encoded = "..."; + * int len = strlen(b64encoded); + * + * struct datastuff foo; + * if (modp_b64_encode_strlen(sizeof(struct datastuff)) != len) { + * // wrong size + * return false; + * } else { + * // safe to do; + * if (modp_b64_decode((char*) &foo, b64encoded, len) == -1) { + * // bad characters + * return false; + * } + * } + * // foo is filled out now + * \endcode + */ +#define modp_b64_encode_strlen(A) ((A + 2)/ 3 * 4) + +#define MODP_B64_ERROR ((size_t)-1) + +#ifdef __cplusplus +} + +#include + +inline std::string& modp_b64_encode(std::string& s) +{ + std::string x(modp_b64_encode_len(s.size()), '\0'); + size_t d = modp_b64_encode(const_cast(x.data()), s.data(), (int)s.size()); + x.erase(d, std::string::npos); + s.swap(x); + return s; +} + +/** + * base 64 decode a string (self-modifing) + * On failure, the string is empty. + * + * This function is for C++ only (duh) + * + * \param[in,out] s the string to be decoded + * \return a reference to the input string + */ +inline std::string& modp_b64_decode(std::string& s) +{ + std::string x(modp_b64_decode_len(s.size()), '\0'); + size_t d = modp_b64_decode(const_cast(x.data()), s.data(), (int)s.size()); + if (d == MODP_B64_ERROR) { + x.clear(); + } else { + x.erase(d, std::string::npos); + } + s.swap(x); + return s; +} + +#endif /* __cplusplus */ + +#endif /* MODP_B64 */ diff --git a/extern/modp_b64/modp_b64_data.h b/extern/modp_b64/modp_b64_data.h new file mode 100644 index 000000000..2ecf5977b --- /dev/null +++ b/extern/modp_b64/modp_b64_data.h @@ -0,0 +1,481 @@ +#include + +#define CHAR62 '+' +#define CHAR63 '/' +#define CHARPAD '=' +static const char e0[256] = { + 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'C', 'C', + 'C', 'C', 'D', 'D', 'D', 'D', 'E', 'E', 'E', 'E', + 'F', 'F', 'F', 'F', 'G', 'G', 'G', 'G', 'H', 'H', + 'H', 'H', 'I', 'I', 'I', 'I', 'J', 'J', 'J', 'J', + 'K', 'K', 'K', 'K', 'L', 'L', 'L', 'L', 'M', 'M', + 'M', 'M', 'N', 'N', 'N', 'N', 'O', 'O', 'O', 'O', + 'P', 'P', 'P', 'P', 'Q', 'Q', 'Q', 'Q', 'R', 'R', + 'R', 'R', 'S', 'S', 'S', 'S', 'T', 'T', 'T', 'T', + 'U', 'U', 'U', 'U', 'V', 'V', 'V', 'V', 'W', 'W', + 'W', 'W', 'X', 'X', 'X', 'X', 'Y', 'Y', 'Y', 'Y', + 'Z', 'Z', 'Z', 'Z', 'a', 'a', 'a', 'a', 'b', 'b', + 'b', 'b', 'c', 'c', 'c', 'c', 'd', 'd', 'd', 'd', + 'e', 'e', 'e', 'e', 'f', 'f', 'f', 'f', 'g', 'g', + 'g', 'g', 'h', 'h', 'h', 'h', 'i', 'i', 'i', 'i', + 'j', 'j', 'j', 'j', 'k', 'k', 'k', 'k', 'l', 'l', + 'l', 'l', 'm', 'm', 'm', 'm', 'n', 'n', 'n', 'n', + 'o', 'o', 'o', 'o', 'p', 'p', 'p', 'p', 'q', 'q', + 'q', 'q', 'r', 'r', 'r', 'r', 's', 's', 's', 's', + 't', 't', 't', 't', 'u', 'u', 'u', 'u', 'v', 'v', + 'v', 'v', 'w', 'w', 'w', 'w', 'x', 'x', 'x', 'x', + 'y', 'y', 'y', 'y', 'z', 'z', 'z', 'z', '0', '0', + '0', '0', '1', '1', '1', '1', '2', '2', '2', '2', + '3', '3', '3', '3', '4', '4', '4', '4', '5', '5', + '5', '5', '6', '6', '6', '6', '7', '7', '7', '7', + '8', '8', '8', '8', '9', '9', '9', '9', '+', '+', + '+', '+', '/', '/', '/', '/' +}; + +static const char e1[256] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', + 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', + 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', + 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', + 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', + 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', + 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', '+', '/' +}; + +static const char e2[256] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', + 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', + 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', + 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', + 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', + 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', + 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', '+', '/' +}; + + + +#ifdef WORDS_BIGENDIAN + + +/* SPECIAL DECODE TABLES FOR BIG ENDIAN (IBM/MOTOROLA/SUN) CPUS */ + +static const uint32_t d0[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x00f80000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00fc0000, +0x00d00000, 0x00d40000, 0x00d80000, 0x00dc0000, 0x00e00000, 0x00e40000, +0x00e80000, 0x00ec0000, 0x00f00000, 0x00f40000, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00040000, 0x00080000, 0x000c0000, 0x00100000, 0x00140000, 0x00180000, +0x001c0000, 0x00200000, 0x00240000, 0x00280000, 0x002c0000, 0x00300000, +0x00340000, 0x00380000, 0x003c0000, 0x00400000, 0x00440000, 0x00480000, +0x004c0000, 0x00500000, 0x00540000, 0x00580000, 0x005c0000, 0x00600000, +0x00640000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x00680000, 0x006c0000, 0x00700000, 0x00740000, 0x00780000, +0x007c0000, 0x00800000, 0x00840000, 0x00880000, 0x008c0000, 0x00900000, +0x00940000, 0x00980000, 0x009c0000, 0x00a00000, 0x00a40000, 0x00a80000, +0x00ac0000, 0x00b00000, 0x00b40000, 0x00b80000, 0x00bc0000, 0x00c00000, +0x00c40000, 0x00c80000, 0x00cc0000, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +static const uint32_t d1[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x0003e000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0003f000, +0x00034000, 0x00035000, 0x00036000, 0x00037000, 0x00038000, 0x00039000, +0x0003a000, 0x0003b000, 0x0003c000, 0x0003d000, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000, +0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000, +0x0000d000, 0x0000e000, 0x0000f000, 0x00010000, 0x00011000, 0x00012000, +0x00013000, 0x00014000, 0x00015000, 0x00016000, 0x00017000, 0x00018000, +0x00019000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x0001a000, 0x0001b000, 0x0001c000, 0x0001d000, 0x0001e000, +0x0001f000, 0x00020000, 0x00021000, 0x00022000, 0x00023000, 0x00024000, +0x00025000, 0x00026000, 0x00027000, 0x00028000, 0x00029000, 0x0002a000, +0x0002b000, 0x0002c000, 0x0002d000, 0x0002e000, 0x0002f000, 0x00030000, +0x00031000, 0x00032000, 0x00033000, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +static const uint32_t d2[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x00000f80, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000fc0, +0x00000d00, 0x00000d40, 0x00000d80, 0x00000dc0, 0x00000e00, 0x00000e40, +0x00000e80, 0x00000ec0, 0x00000f00, 0x00000f40, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00000040, 0x00000080, 0x000000c0, 0x00000100, 0x00000140, 0x00000180, +0x000001c0, 0x00000200, 0x00000240, 0x00000280, 0x000002c0, 0x00000300, +0x00000340, 0x00000380, 0x000003c0, 0x00000400, 0x00000440, 0x00000480, +0x000004c0, 0x00000500, 0x00000540, 0x00000580, 0x000005c0, 0x00000600, +0x00000640, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x00000680, 0x000006c0, 0x00000700, 0x00000740, 0x00000780, +0x000007c0, 0x00000800, 0x00000840, 0x00000880, 0x000008c0, 0x00000900, +0x00000940, 0x00000980, 0x000009c0, 0x00000a00, 0x00000a40, 0x00000a80, +0x00000ac0, 0x00000b00, 0x00000b40, 0x00000b80, 0x00000bc0, 0x00000c00, +0x00000c40, 0x00000c80, 0x00000cc0, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +static const uint32_t d3[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x0000003e, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000003f, +0x00000034, 0x00000035, 0x00000036, 0x00000037, 0x00000038, 0x00000039, +0x0000003a, 0x0000003b, 0x0000003c, 0x0000003d, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00000001, 0x00000002, 0x00000003, 0x00000004, 0x00000005, 0x00000006, +0x00000007, 0x00000008, 0x00000009, 0x0000000a, 0x0000000b, 0x0000000c, +0x0000000d, 0x0000000e, 0x0000000f, 0x00000010, 0x00000011, 0x00000012, +0x00000013, 0x00000014, 0x00000015, 0x00000016, 0x00000017, 0x00000018, +0x00000019, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x0000001a, 0x0000001b, 0x0000001c, 0x0000001d, 0x0000001e, +0x0000001f, 0x00000020, 0x00000021, 0x00000022, 0x00000023, 0x00000024, +0x00000025, 0x00000026, 0x00000027, 0x00000028, 0x00000029, 0x0000002a, +0x0000002b, 0x0000002c, 0x0000002d, 0x0000002e, 0x0000002f, 0x00000030, +0x00000031, 0x00000032, 0x00000033, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +#else + + +/* SPECIAL DECODE TABLES FOR LITTLE ENDIAN (INTEL) CPUS */ + +static const uint32_t d0[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x000000f8, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000fc, +0x000000d0, 0x000000d4, 0x000000d8, 0x000000dc, 0x000000e0, 0x000000e4, +0x000000e8, 0x000000ec, 0x000000f0, 0x000000f4, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00000004, 0x00000008, 0x0000000c, 0x00000010, 0x00000014, 0x00000018, +0x0000001c, 0x00000020, 0x00000024, 0x00000028, 0x0000002c, 0x00000030, +0x00000034, 0x00000038, 0x0000003c, 0x00000040, 0x00000044, 0x00000048, +0x0000004c, 0x00000050, 0x00000054, 0x00000058, 0x0000005c, 0x00000060, +0x00000064, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x00000068, 0x0000006c, 0x00000070, 0x00000074, 0x00000078, +0x0000007c, 0x00000080, 0x00000084, 0x00000088, 0x0000008c, 0x00000090, +0x00000094, 0x00000098, 0x0000009c, 0x000000a0, 0x000000a4, 0x000000a8, +0x000000ac, 0x000000b0, 0x000000b4, 0x000000b8, 0x000000bc, 0x000000c0, +0x000000c4, 0x000000c8, 0x000000cc, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +static const uint32_t d1[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x0000e003, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000f003, +0x00004003, 0x00005003, 0x00006003, 0x00007003, 0x00008003, 0x00009003, +0x0000a003, 0x0000b003, 0x0000c003, 0x0000d003, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000, +0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000, +0x0000d000, 0x0000e000, 0x0000f000, 0x00000001, 0x00001001, 0x00002001, +0x00003001, 0x00004001, 0x00005001, 0x00006001, 0x00007001, 0x00008001, +0x00009001, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x0000a001, 0x0000b001, 0x0000c001, 0x0000d001, 0x0000e001, +0x0000f001, 0x00000002, 0x00001002, 0x00002002, 0x00003002, 0x00004002, +0x00005002, 0x00006002, 0x00007002, 0x00008002, 0x00009002, 0x0000a002, +0x0000b002, 0x0000c002, 0x0000d002, 0x0000e002, 0x0000f002, 0x00000003, +0x00001003, 0x00002003, 0x00003003, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +static const uint32_t d2[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x00800f00, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00c00f00, +0x00000d00, 0x00400d00, 0x00800d00, 0x00c00d00, 0x00000e00, 0x00400e00, +0x00800e00, 0x00c00e00, 0x00000f00, 0x00400f00, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00400000, 0x00800000, 0x00c00000, 0x00000100, 0x00400100, 0x00800100, +0x00c00100, 0x00000200, 0x00400200, 0x00800200, 0x00c00200, 0x00000300, +0x00400300, 0x00800300, 0x00c00300, 0x00000400, 0x00400400, 0x00800400, +0x00c00400, 0x00000500, 0x00400500, 0x00800500, 0x00c00500, 0x00000600, +0x00400600, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x00800600, 0x00c00600, 0x00000700, 0x00400700, 0x00800700, +0x00c00700, 0x00000800, 0x00400800, 0x00800800, 0x00c00800, 0x00000900, +0x00400900, 0x00800900, 0x00c00900, 0x00000a00, 0x00400a00, 0x00800a00, +0x00c00a00, 0x00000b00, 0x00400b00, 0x00800b00, 0x00c00b00, 0x00000c00, +0x00400c00, 0x00800c00, 0x00c00c00, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +static const uint32_t d3[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x003e0000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003f0000, +0x00340000, 0x00350000, 0x00360000, 0x00370000, 0x00380000, 0x00390000, +0x003a0000, 0x003b0000, 0x003c0000, 0x003d0000, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00010000, 0x00020000, 0x00030000, 0x00040000, 0x00050000, 0x00060000, +0x00070000, 0x00080000, 0x00090000, 0x000a0000, 0x000b0000, 0x000c0000, +0x000d0000, 0x000e0000, 0x000f0000, 0x00100000, 0x00110000, 0x00120000, +0x00130000, 0x00140000, 0x00150000, 0x00160000, 0x00170000, 0x00180000, +0x00190000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x001a0000, 0x001b0000, 0x001c0000, 0x001d0000, 0x001e0000, +0x001f0000, 0x00200000, 0x00210000, 0x00220000, 0x00230000, 0x00240000, +0x00250000, 0x00260000, 0x00270000, 0x00280000, 0x00290000, 0x002a0000, +0x002b0000, 0x002c0000, 0x002d0000, 0x002e0000, 0x002f0000, 0x00300000, +0x00310000, 0x00320000, 0x00330000, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +#endif diff --git a/extern/stb b/extern/stb new file mode 160000 index 000000000..b42009b3b --- /dev/null +++ b/extern/stb @@ -0,0 +1 @@ +Subproject commit b42009b3b9d4ca35bc703f5310eedc74f584be58 diff --git a/extern/tinygltf b/extern/tinygltf deleted file mode 160000 index 18f0e20a1..000000000 --- a/extern/tinygltf +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 18f0e20a118409c0df0f33252549586cae7d2df0 diff --git a/tools/generate-gltf-classes/.gitignore b/tools/generate-gltf-classes/.gitignore new file mode 100644 index 000000000..3e6e1184f --- /dev/null +++ b/tools/generate-gltf-classes/.gitignore @@ -0,0 +1,2 @@ +node_modules +test_output diff --git a/tools/generate-gltf-classes/.vscode/launch.json b/tools/generate-gltf-classes/.vscode/launch.json new file mode 100644 index 000000000..f94c85919 --- /dev/null +++ b/tools/generate-gltf-classes/.vscode/launch.json @@ -0,0 +1,23 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "pwa-node", + "request": "launch", + "name": "Launch Program", + "skipFiles": [ + "/**" + ], + "program": "${workspaceFolder}\\index.js", + "args": [ + "--schema", "../../extern/glTF/specification/2.0/schema/", + "--output", "test_output/model", + "--readerOutput", "test_output/reader", + "--extensions", "../../extern/glTF/extensions/2.0" + ] + } + ] +} \ No newline at end of file diff --git a/tools/generate-gltf-classes/SchemaCache.js b/tools/generate-gltf-classes/SchemaCache.js new file mode 100644 index 000000000..d89f8f926 --- /dev/null +++ b/tools/generate-gltf-classes/SchemaCache.js @@ -0,0 +1,34 @@ +const path = require("path"); +const fs = require("fs"); + +class SchemaCache { + constructor(schemaPath, extensionPath) { + this.schemaPath = schemaPath; + this.extensionPath = extensionPath; + this.cache = {}; + } + + load(name) { + const existing = this.cache[name]; + if (existing) { + return existing; + } + + const result = JSON.parse(fs.readFileSync(path.join(this.schemaPath, name), "utf-8")); + this.cache[name] = result; + return result; + } + + loadExtension(name) { + const existing = this.cache[name]; + if (existing) { + return existing; + } + + const result = JSON.parse(fs.readFileSync(path.join(this.extensionPath, name), "utf-8")); + this.cache[name] = result; + return result; + } +} + +module.exports = SchemaCache; diff --git a/tools/generate-gltf-classes/createExtensionsProperty.js b/tools/generate-gltf-classes/createExtensionsProperty.js new file mode 100644 index 000000000..17e916637 --- /dev/null +++ b/tools/generate-gltf-classes/createExtensionsProperty.js @@ -0,0 +1,56 @@ +const unindent = require("./unindent"); + +function createExtensionsProperty(extensions, name, schema) { + if (!extensions) { + return undefined; + } + + return { + name: "extensions", + headers: [], + readerHeaders: extensions.map(extension => `"${extension.className}JsonHandler.h"`), + readerHeadersImpl: extensions.map(extension => `"CesiumGltf/${extension.className}.h"`), + type: undefined, + readerType: "ExtensionsJsonHandler", + schemas: [], + localTypes: [], + readerLocalTypes: [createExtensionType(extensions)], + readerLocalTypesImpl: [createExtensionTypeImpl(name, extensions)], + briefDoc: undefined, + fullDoc: undefined + }; +} + +function createExtensionType(extensions) { + return unindent(` + class ExtensionsJsonHandler : public ObjectJsonHandler { + public: + void reset(IJsonHandler* pParent, std::vector* pExtensions); + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + private: + std::vector* _pExtensions = nullptr; + ${extensions.map(extension => `${extension.className}JsonHandler _${extension.name};`).join("\n")} + }; + `); + return "Extensions!!"; +} + +function createExtensionTypeImpl(name, extensions) { + return unindent(` + void ${name}JsonHandler::ExtensionsJsonHandler::reset(IJsonHandler* pParent, std::vector* pExtensions) { + ObjectJsonHandler::reset(pParent); + this->_pExtensions = pExtensions; + } + + IJsonHandler* ${name}JsonHandler::ExtensionsJsonHandler::Key(const char* str, size_t /* length */, bool /* copy */) { + using namespace std::string_literals; + + ${extensions.map(extension => `if ("${extension.name}"s == str) return property("${extension.name}", this->_${extension.name}, std::any_cast<${extension.className}&>(this->_pExtensions->emplace_back(${extension.className}())));`).join("\n")} + + return this->ignoreAndContinue(); + } + `); +} + +module.exports = createExtensionsProperty; diff --git a/tools/generate-gltf-classes/generate.js b/tools/generate-gltf-classes/generate.js new file mode 100644 index 000000000..e988ab17e --- /dev/null +++ b/tools/generate-gltf-classes/generate.js @@ -0,0 +1,240 @@ +const createExtensionsProperty = require("./createExtensionsProperty"); +const fs = require("fs"); +const getNameFromSchema = require("./getNameFromSchema"); +const indent = require("./indent"); +const lodash = require("lodash"); +const path = require("path"); +const resolveProperty = require("./resolveProperty"); +const unindent = require("./unindent"); + +function generate(options, schema) { + const { schemaCache, config, outputDir, readerOutputDir } = options; + + const name = getNameFromSchema(config, schema); + const thisConfig = config.classes[schema.title] || {}; + + console.log(`Generating ${name}`); + + let base = "ExtensibleObject"; + if (schema.allOf && schema.allOf.length > 0 && schema.allOf[0].$ref) { + const baseSchema = schemaCache.load(schema.allOf[0].$ref); + base = getNameFromSchema(config, baseSchema); + } + + const required = schema.required || []; + + const properties = Object.keys(schema.properties) + .map((key) => + resolveProperty(schemaCache, config, name, key, schema.properties[key], required) + ) + .filter((property) => property !== undefined); + + const extensionsProperty = createExtensionsProperty(options.extensions[schema.title], name, schema); + if (extensionsProperty) { + properties.push(extensionsProperty); + } + + const localTypes = lodash.uniq( + lodash.flatten(properties.map((property) => property.localTypes)) + ); + + const headers = lodash.uniq([ + `"CesiumGltf/Library.h"`, + `"CesiumGltf/${base}.h"`, + ...lodash.flatten(properties.map((property) => property.headers)) + ]); + + headers.sort(); + + const header = ` + // This file was generated by generate-gltf-classes. + // DO NOT EDIT THIS FILE! + #pragma once + + ${headers.map((header) => `#include ${header}`).join("\n")} + + namespace CesiumGltf { + /** + * @brief ${schema.description} + */ + struct CESIUMGLTF_API ${name}${thisConfig.toBeInherited ? "Spec" : (thisConfig.isBaseClass ? "" : " final")} : public ${base} { + ${indent(localTypes.join("\n\n"), 16)} + + ${indent( + properties + .map((property) => formatProperty(property)) + .filter(propertyText => propertyText !== undefined) + .join("\n\n"), + 16 + )} + ${thisConfig.toBeInherited ? privateSpecConstructor(name) : ""} + }; + } + `; + + const headerOutputDir = path.join(outputDir, "include", "CesiumGltf"); + fs.mkdirSync(headerOutputDir, { recursive: true }); + const headerOutputPath = path.join(headerOutputDir, `${name}${thisConfig.toBeInherited ? "Spec" : ""}.h`); + fs.writeFileSync(headerOutputPath, unindent(header), "utf-8"); + + const readerHeaders = lodash.uniq( + [`"${base}JsonHandler.h"`, ...lodash.flatten(properties.map((property) => property.readerHeaders))] + ); + readerHeaders.sort(); + + const readerLocalTypes = lodash.uniq( + lodash.flatten(properties.map((property) => property.readerLocalTypes)) + ); + + const readerHeader = ` + // This file was generated by generate-gltf-classes. + // DO NOT EDIT THIS FILE! + #pragma once + + ${readerHeaders.map((header) => `#include ${header}`).join("\n")} + + namespace CesiumGltf { + struct ${name}; + + class ${name}JsonHandler : public ${base}JsonHandler { + public: + void reset(IJsonHandler* pHandler, ${name}* pObject); + ${name}* getObject(); + virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override; + + virtual IJsonHandler* Key(const char* str, size_t length, bool copy) override; + + protected: + IJsonHandler* ${name}Key(const char* str, ${name}& o); + + private: + ${indent(readerLocalTypes.join("\n\n"), 12)} + + ${name}* _pObject; + ${indent( + properties + .map((property) => formatReaderProperty(property)) + .join("\n"), + 12 + )} + }; + } + `; + + const readerHeaderOutputDir = path.join(readerOutputDir, "generated"); + fs.mkdirSync(readerHeaderOutputDir, { recursive: true }); + const readerHeaderOutputPath = path.join(readerHeaderOutputDir, name + "JsonHandler.h"); + fs.writeFileSync(readerHeaderOutputPath, unindent(readerHeader), "utf-8"); + + const readerLocalTypesImpl = lodash.uniq( + lodash.flatten(properties.map((property) => property.readerLocalTypesImpl)) + ); + + const readerHeadersImpl = lodash.uniq( + [...lodash.flatten(properties.map((property) => property.readerHeadersImpl))] + ); + readerHeadersImpl.sort(); + + const readerImpl = ` + // This file was generated by generate-gltf-classes. + // DO NOT EDIT THIS FILE! + #include "${name}JsonHandler.h" + #include "CesiumGltf/${name}.h" + ${readerHeadersImpl.map((header) => `#include ${header}`).join("\n")} + #include + #include + + using namespace CesiumGltf; + + void ${name}JsonHandler::reset(IJsonHandler* pParent, ${name}* pObject) { + ${base}JsonHandler::reset(pParent, pObject); + this->_pObject = pObject; + } + + ${name}* ${name}JsonHandler::getObject() { + return this->_pObject; + } + + void ${name}JsonHandler::reportWarning(const std::string& warning, std::vector&& context) { + if (this->getCurrentKey()) { + context.emplace_back(std::string(".") + this->getCurrentKey()); + } + this->parent()->reportWarning(warning, std::move(context)); + } + + IJsonHandler* ${name}JsonHandler::Key(const char* str, size_t /*length*/, bool /*copy*/) { + assert(this->_pObject); + return this->${name}Key(str, *this->_pObject); + } + + IJsonHandler* ${name}JsonHandler::${name}Key(const char* str, ${name}& o) { + using namespace std::string_literals; + + ${indent( + properties + .map((property) => formatReaderPropertyImpl(property)) + .join("\n"), + 10 + )} + + return this->${base}Key(str, *this->_pObject); + } + + ${indent(readerLocalTypesImpl.join("\n\n"), 8)} + `; + + const readerSourceOutputPath = path.join(readerHeaderOutputDir, name + "JsonHandler.cpp"); + fs.writeFileSync(readerSourceOutputPath, unindent(readerImpl), "utf-8"); + + return lodash.uniq( + lodash.flatten(properties.map((property) => property.schemas)) + ); +} + +function formatProperty(property) { + if (!property.type) { + return undefined; + } + + let result = ""; + + result += `/**\n * @brief ${property.briefDoc || property.name}\n`; + if (property.fullDoc) { + result += ` *\n * ${property.fullDoc.split("\n").join("\n * ")}\n`; + } + + result += ` */\n`; + + result += `${property.type} ${property.name}`; + + if (property.defaultValue !== undefined) { + result += " = " + property.defaultValue; + } else if (property.needsInitialization) { + result += " = " + property.type + "()"; + } + + result += ";"; + + return result; +} + +function formatReaderProperty(property) { + return `${property.readerType} _${property.name};` +} + +function formatReaderPropertyImpl(property) { + return `if ("${property.name}"s == str) return property("${property.name}", this->_${property.name}, o.${property.name});`; +} + +function privateSpecConstructor(name) { + return ` + private: + /** + * @brief This class is not mean to be instantiated directly. Use {@link ${name}} instead. + */ + ${name}Spec() = default; + friend struct ${name}; + `; +} + +module.exports = generate; diff --git a/tools/generate-gltf-classes/getNameFromSchema.js b/tools/generate-gltf-classes/getNameFromSchema.js new file mode 100644 index 000000000..c864ffd8a --- /dev/null +++ b/tools/generate-gltf-classes/getNameFromSchema.js @@ -0,0 +1,6 @@ +function getNameFromSchema(config, schema) { + const title = schema.title; + return config.classes[title] && config.classes[title].overrideName ? config.classes[title].overrideName : title.replace(/\s/g, ""); +} + +module.exports = getNameFromSchema; \ No newline at end of file diff --git a/tools/generate-gltf-classes/glTF.json b/tools/generate-gltf-classes/glTF.json new file mode 100644 index 000000000..91d74ee05 --- /dev/null +++ b/tools/generate-gltf-classes/glTF.json @@ -0,0 +1,36 @@ +{ + "classes": { + "glTF": { + "overrideName": "Model", + "toBeInherited": true + }, + "glTF Property": { + "overrideName": "ExtensibleObject" + }, + "glTF Child of Root Property": { + "overrideName": "NamedObject" + }, + "Buffer": { + "toBeInherited": true + }, + "Image": { + "toBeInherited": true + }, + "Accessor": { + "toBeInherited": true + }, + "Texture Info": { + "isBaseClass": true + } + }, + "extensions": [ + { + "className": "KHR_draco_mesh_compression", + "extensionName": "KHR_draco_mesh_compression", + "schema": "Khronos/KHR_draco_mesh_compression/schema/mesh.primitive.KHR_draco_mesh_compression.schema.json", + "attachTo": [ + "mesh.primitive" + ] + } + ] +} diff --git a/tools/generate-gltf-classes/indent.js b/tools/generate-gltf-classes/indent.js new file mode 100644 index 000000000..e32faf938 --- /dev/null +++ b/tools/generate-gltf-classes/indent.js @@ -0,0 +1,10 @@ +function indent(text, spaces) { + let indentText = ""; + for (let i = 0; i < spaces; ++i) { + indentText += " "; + } + + return text.replace(new RegExp("\r?\n", "gm"), "\n" + indentText); +} + +module.exports = indent; \ No newline at end of file diff --git a/tools/generate-gltf-classes/index.js b/tools/generate-gltf-classes/index.js new file mode 100644 index 000000000..cd3d0a1c9 --- /dev/null +++ b/tools/generate-gltf-classes/index.js @@ -0,0 +1,98 @@ +const yargs = require("yargs"); +const path = require("path"); +const fs = require("fs"); +const SchemaCache = require("./SchemaCache"); +const generate = require("./generate"); + +const argv = yargs.options({ + schema: { + alias: "s", + description: "The path to the glTF 2.0 JSONSchema files.", + demandOption: true, + type: "string" + }, + output: { + alias: "o", + description: "The output directory for the generated glTF class files.", + demandOption: true, + type: "string" + }, + readerOutput: { + alias: "r", + description: "The output directory for the generated reader files.", + demandOption: true, + type: "string" + }, + extensions: { + alias: "e", + description: "The extensions directory.", + demandOption: true, + type: "string" + }, + config: { + alias: "c", + description: "The path to the configuration options controlling code generation, expressed in a JSON file.", + demandOption: true, + type: "string" + } +}).argv; + +const schemaCache = new SchemaCache(argv.schema, argv.extensions); +const modelSchema = schemaCache.load("glTF.schema.json"); + +const config = JSON.parse(fs.readFileSync(argv.config, "utf-8")); + +const options = { + schemaCache, + outputDir: argv.output, + readerOutputDir: argv.readerOutput, + config: config, + // key: Title of the element name that is extended (e.g. "Mesh Primitive") + // value: Array of extension type names. + extensions: {} +}; + +let schemas = [modelSchema]; + +for (const extension of config.extensions) { + const extensionSchema = schemaCache.loadExtension(extension.schema); + if (!extensionSchema) { + console.warn(`Could not load schema ${extension.schema} for extension class ${extension.className}.`); + continue; + } + + if (!config.classes[extensionSchema.title]) { + config.classes[extensionSchema.title] = {}; + } + config.classes[extensionSchema.title].overrideName = extension.className; + + schemas.push(...generate(options, extensionSchema)); + + for (const objectToExtend of extension.attachTo) { + const objectToExtendSchema = schemaCache.load(`${objectToExtend}.schema.json`); + if (!objectToExtendSchema) { + console.warn("Could not load schema for ${objectToExtend}."); + continue; + } + + if (!options.extensions[objectToExtend]) { + options.extensions[objectToExtendSchema.title] = []; + } + + options.extensions[objectToExtendSchema.title].push({ + name: extension.extensionName, + className: extension.className + }); + } +} + +const processed = {}; + +while (schemas.length > 0) { + const schema = schemas.pop(); + if (processed[schema.title]) { + continue; + } + processed[schema.title] = true; + schemas.push(...generate(options, schema)); +} diff --git a/tools/generate-gltf-classes/makeIdentifier.js b/tools/generate-gltf-classes/makeIdentifier.js new file mode 100644 index 000000000..ab541f768 --- /dev/null +++ b/tools/generate-gltf-classes/makeIdentifier.js @@ -0,0 +1,5 @@ +function makeIdentifier(s) { + return s.replace(/\//g, "_"); +} + +module.exports = makeIdentifier; diff --git a/tools/generate-gltf-classes/package-lock.json b/tools/generate-gltf-classes/package-lock.json new file mode 100644 index 000000000..28dd5ea5e --- /dev/null +++ b/tools/generate-gltf-classes/package-lock.json @@ -0,0 +1,132 @@ +{ + "name": "generate-gltf-classes", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + }, + "prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz", + "integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==" + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==" + } + } +} diff --git a/tools/generate-gltf-classes/package.json b/tools/generate-gltf-classes/package.json new file mode 100644 index 000000000..5b17ed7a5 --- /dev/null +++ b/tools/generate-gltf-classes/package.json @@ -0,0 +1,19 @@ +{ + "name": "generate-gltf-classes", + "version": "1.0.0", + "description": "Generate CesiumGltf C++ classes from the glTF spec.", + "main": "index.js", + "scripts": { + "test": "node index.js --schema ../../extern/glTF/specification/2.0/schema/ --output test_output/model --readerOutput test_output/reader --extensions ../../extern/glTF/extensions/2.0 --config glTF.json", + "generate": "node index.js --schema ../../extern/glTF/specification/2.0/schema/ --output ../../CesiumGltf --readerOutput ../../CesiumGltfReader --extensions ../../extern/glTF/extensions/2.0 --config glTF.json" + }, + "author": "CesiumGS, Inc.", + "license": "UNLICENSED", + "dependencies": { + "lodash": "^4.17.20", + "yargs": "^16.2.0" + }, + "devDependencies": { + "prettier": "^2.2.1" + } +} diff --git a/tools/generate-gltf-classes/resolveProperty.js b/tools/generate-gltf-classes/resolveProperty.js new file mode 100644 index 000000000..4e37551e2 --- /dev/null +++ b/tools/generate-gltf-classes/resolveProperty.js @@ -0,0 +1,398 @@ +const getNameFromSchema = require("./getNameFromSchema"); +const unindent = require("./unindent"); +const indent = require("./indent"); +const makeIdentifier = require("./makeIdentifier"); + +function resolveProperty( + schemaCache, + config, + parentName, + propertyName, + propertyDetails, + required +) { + if (Object.keys(propertyDetails).length === 0) { + // Ignore totally empty properties. + return undefined; + } + + // If we don't know what's required, act as if everything is. + // Specifically this means we _don't_ make it optional. + const isRequired = required === undefined || required.includes(propertyName); + const makeOptional = !isRequired && propertyDetails.default === undefined; + + if (propertyDetails.type == "array") { + return resolveArray( + schemaCache, + config, + parentName, + propertyName, + propertyDetails, + required + ); + } else if (propertyDetails.type == "integer") { + return { + ...propertyDefaults(propertyName, propertyDetails), + headers: ["", ...(makeOptional ? [""] : [])], + type: makeOptional ? "std::optional" : "int64_t", + readerHeaders: [`"IntegerJsonHandler.h"`], + readerType: "IntegerJsonHandler", + needsInitialization: !makeOptional + }; + } else if (propertyDetails.type == "number") { + return { + ...propertyDefaults(propertyName, propertyDetails), + headers: makeOptional ? [""] : [], + type: makeOptional ? "std::optional" : "double", + readerHeaders: [`"DoubleJsonHandler.h"`], + readerType: "DoubleJsonHandler", + needsInitialization: !makeOptional + }; + } else if (propertyDetails.type == "boolean") { + return { + ...propertyDefaults(propertyName, propertyDetails), + headers: makeOptional ? [""] : [], + type: makeOptional ? "std::optional" : "bool", + readerHeaders: `"BoolJsonHandler.h"`, + readerType: "BoolJsonHandler", + needsInitialization: ~makeOptional + }; + } else if (propertyDetails.type == "string") { + return { + ...propertyDefaults(propertyName, propertyDetails), + type: makeOptional ? "std::optional" : "std::string", + headers: ["", ...(makeOptional ? [""] : [])], + readerHeaders: [`"StringJsonHandler.h"`], + readerType: "StringJsonHandler", + }; + } else if ( + propertyDetails.type == "object" && + propertyDetails.additionalProperties + ) { + return resolveDictionary( + schemaCache, + config, + parentName, + propertyName, + propertyDetails, + required + ); + } else if ( + propertyDetails.anyOf && + propertyDetails.anyOf.length > 0 && + propertyDetails.anyOf[0].enum + ) { + return resolveEnum( + schemaCache, + config, + parentName, + propertyName, + propertyDetails, + makeOptional + ); + } else if (propertyDetails.$ref) { + const itemSchema = schemaCache.load(propertyDetails.$ref); + if (itemSchema.title === "glTF Id") { + return { + ...propertyDefaults(propertyName, propertyDetails), + type: "int32_t", + defaultValue: -1, + headers: [""], + readerHeaders: [`"IntegerJsonHandler.h"`], + readerType: "IntegerJsonHandler", + }; + } else { + const type = getNameFromSchema(config, itemSchema); + const typeName = getNameFromSchema(config, itemSchema); + + return { + ...propertyDefaults(propertyName, propertyDetails), + type: makeOptional ? `std::optional<${typeName}>` : typeName, + headers: [`"CesiumGltf/${type}.h"`, ...(makeOptional ? [""] : [])], + readerType: `${type}JsonHandler`, + readerHeaders: [`"${type}JsonHandler.h"`], + schemas: [itemSchema], + }; + } + } else if (propertyDetails.allOf && propertyDetails.allOf.length == 1) { + const nested = resolveProperty( + schemaCache, + config, + parentName, + propertyName, + propertyDetails.allOf[0], + required, + ); + + return { + ...nested, + briefDoc: propertyDefaults(propertyName, propertyDetails).briefDoc, + fullDoc: propertyDefaults(propertyName, propertyDetails).fullDoc, + }; + } else { + console.warn(`Skipping unhandled property ${propertyName}.`); + return undefined; + } +} + +function toPascalCase(name) { + if (name.length === 0) { + return name; + } + + return name[0].toUpperCase() + name.substr(1); +} + +function propertyDefaults(propertyName, propertyDetails) { + const fullDoc = + propertyDetails.gltf_detailedDescription && + propertyDetails.gltf_detailedDescription.indexOf( + propertyDetails.description + ) === 0 + ? propertyDetails.gltf_detailedDescription + .substr(propertyDetails.description.length) + .trim() + : propertyDetails.gltf_detailedDescription; + return { + name: propertyName, + headers: [], + readerHeaders: [], + readerHeadersImpl: [], + type: "", + defaultValue: propertyDetails.default !== undefined ? propertyDetails.default.toString() : undefined, + readerType: "", + schemas: [], + localTypes: [], + readerLocalTypes: [], + readerLocalTypesImpl: [], + briefDoc: propertyDetails.description, + fullDoc: fullDoc, + }; +} + +function resolveArray( + schemaCache, + config, + parentName, + propertyName, + propertyDetails, + required +) { + const itemProperty = resolveProperty( + schemaCache, + config, + parentName, + propertyName + ".items", + propertyDetails.items, + undefined + ); + + if (!itemProperty) { + return undefined; + } + + return { + ...propertyDefaults(propertyName, propertyDetails), + name: propertyName, + headers: ["", ...itemProperty.headers], + schemas: itemProperty.schemas, + localTypes: itemProperty.localTypes, + type: `std::vector<${itemProperty.type}>`, + defaultValue: propertyDetails.default ? `{ ${propertyDetails.default} }` : undefined, + readerHeaders: [`"ArrayJsonHandler.h"`, ...itemProperty.readerHeaders], + readerType: `ArrayJsonHandler<${itemProperty.type}, ${itemProperty.readerType}>`, + }; +} + +function resolveDictionary( + schemaCache, + config, + parentName, + propertyName, + propertyDetails, + required +) { + const additional = resolveProperty( + schemaCache, + config, + parentName, + propertyName + ".additionalProperties", + propertyDetails.additionalProperties, + required + ); + + if (!additional) { + return undefined; + } + + return { + ...propertyDefaults(propertyName, propertyDetails), + name: propertyName, + headers: ["", ...additional.headers], + schema: additional.schemas, + localTypes: additional.localTypes, + type: `std::unordered_map`, + readerHeaders: [`"DictionaryJsonHandler.h"`, ...additional.readerHeaders], + readerType: `DictionaryJsonHandler<${additional.type}, ${additional.readerType}>`, + }; +} + +function resolveEnum( + schemaCache, + config, + parentName, + propertyName, + propertyDetails, + makeOptional +) { + if ( + !propertyDetails.anyOf || + propertyDetails.anyOf.length === 0 || + !propertyDetails.anyOf[0].enum || + propertyDetails.anyOf[0].enum.length === 0 + ) { + return undefined; + } + + const enumName = toPascalCase(propertyName); + + const readerTypes = createEnumReaderType( + parentName, + enumName, + propertyName, + propertyDetails + ); + + const result = { + ...propertyDefaults(propertyName, propertyDetails), + localTypes: [ + unindent(` + enum class ${toPascalCase(propertyName)} { + ${indent( + propertyDetails.anyOf + .map((e) => createEnum(e)) + .filter((e) => e !== undefined) + .join(",\n\n"), + 12 + )} + }; + `), + ], + type: makeOptional ? `std::optional<${enumName}>` : enumName, + headers: makeOptional ? [""] : [], + defaultValue: createEnumDefault(enumName, propertyDetails), + readerHeaders: [`"CesiumGltf/${parentName}.h"`], + readerLocalTypes: readerTypes, + readerLocalTypesImpl: createEnumReaderTypeImpl( + parentName, + enumName, + propertyName, + propertyDetails + ), + needsInitialization: !makeOptional + }; + + if (readerTypes.length > 0) { + result.readerType = `${enumName}JsonHandler`; + } else { + result.readerType = `IntegerJsonHandler<${parentName}::${enumName}>`; + result.readerHeaders.push(`"IntegerJsonHandler.h"`); + } + + return result; +} + +function createEnumDefault(enumName, propertyDetails) { + if (propertyDetails.default === undefined) { + return undefined; + } + + if (propertyDetails.anyOf[0].type === "integer") { + return `${enumName}(${propertyDetails.default})`; + } else { + return `${enumName}::${propertyDetails.default}`; + } +} + +function createEnum(enumDetails) { + if (!enumDetails.enum || enumDetails.enum.length === 0) { + return undefined; + } + + if (enumDetails.type === "integer") { + return `${makeIdentifier(enumDetails.description)} = ${ + enumDetails.enum[0] + }`; + } else { + return makeIdentifier(enumDetails.enum[0]); + } +} + +function createEnumReaderType( + parentName, + enumName, + propertyName, + propertyDetails +) { + if (propertyDetails.anyOf[0].type === "integer") { + // No special reader needed for integer enums. + return []; + } + + return unindent(` + class ${enumName}JsonHandler : public JsonHandler { + public: + void reset(IJsonHandler* pParent, ${parentName}::${enumName}* pEnum); + virtual IJsonHandler* String(const char* str, size_t length, bool copy) override; + + private: + ${parentName}::${enumName}* _pEnum = nullptr; + }; + `); +} + +function createEnumReaderTypeImpl( + parentName, + enumName, + propertyName, + propertyDetails +) { + if (propertyDetails.anyOf[0].type === "integer") { + // No special reader needed for integer enums. + return []; + } + + return unindent(` + void ${parentName}JsonHandler::${enumName}JsonHandler::reset(IJsonHandler* pParent, ${parentName}::${enumName}* pEnum) { + JsonHandler::reset(pParent); + this->_pEnum = pEnum; + } + + IJsonHandler* ${parentName}JsonHandler::${enumName}JsonHandler::String(const char* str, size_t /*length*/, bool /*copy*/) { + using namespace std::string_literals; + + assert(this->_pEnum); + + ${indent( + propertyDetails.anyOf + .map((e) => + e.enum && e.enum[0] !== undefined + ? `if ("${ + e.enum[0] + }"s == str) *this->_pEnum = ${parentName}::${enumName}::${makeIdentifier( + e.enum[0] + )};` + : undefined + ) + .filter((s) => s !== undefined) + .join("\nelse "), + 6 + )} + else return nullptr; + + return this->parent(); + } + `); +} + +module.exports = resolveProperty; diff --git a/tools/generate-gltf-classes/unindent.js b/tools/generate-gltf-classes/unindent.js new file mode 100644 index 000000000..03a59a5ed --- /dev/null +++ b/tools/generate-gltf-classes/unindent.js @@ -0,0 +1,23 @@ +function unindent(indentedText) { + let skip = 0; + let indent = 0; + let spaces = ""; + + for (let i = 0; i < indentedText.length; ++i) { + if (indentedText[i] == "\n" || indentedText[i] == "\r") { + ++skip; + } else if (indentedText[i] == " ") { + ++indent; + spaces += " "; + } else { + break; + } + } + + const regex = new RegExp("^(" + spaces + ")", "gm"); + const lastLine = new RegExp("^\\s+$", "gm"); + const unindented = indentedText.substr(skip).replace(regex, "").replace(lastLine, ""); + return unindented; + } + + module.exports = unindent;