From c27f4ed544b423cec0cda4c85a9c018611126a5a Mon Sep 17 00:00:00 2001 From: Jan Date: Sun, 31 Mar 2024 22:46:31 +0200 Subject: [PATCH] chore: add output implementations for gltf and glb --- src/ObjCommon/XModel/Gltf/GltfConstants.h | 20 +++++ src/ObjWriting.lua | 2 + src/ObjWriting/XModel/Gltf/GltfBinOutput.cpp | 86 +++++++++++++++++++ src/ObjWriting/XModel/Gltf/GltfBinOutput.h | 25 ++++++ src/ObjWriting/XModel/Gltf/GltfOutput.h | 25 ++++++ src/ObjWriting/XModel/Gltf/GltfTextOutput.cpp | 48 +++++++++++ src/ObjWriting/XModel/Gltf/GltfTextOutput.h | 22 +++++ 7 files changed, 228 insertions(+) create mode 100644 src/ObjCommon/XModel/Gltf/GltfConstants.h create mode 100644 src/ObjWriting/XModel/Gltf/GltfBinOutput.cpp create mode 100644 src/ObjWriting/XModel/Gltf/GltfBinOutput.h create mode 100644 src/ObjWriting/XModel/Gltf/GltfOutput.h create mode 100644 src/ObjWriting/XModel/Gltf/GltfTextOutput.cpp create mode 100644 src/ObjWriting/XModel/Gltf/GltfTextOutput.h diff --git a/src/ObjCommon/XModel/Gltf/GltfConstants.h b/src/ObjCommon/XModel/Gltf/GltfConstants.h new file mode 100644 index 00000000..e8d3001a --- /dev/null +++ b/src/ObjCommon/XModel/Gltf/GltfConstants.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Utils/FileUtils.h" + +#include + +namespace gltf +{ + constexpr uint32_t GLTF_MAGIC = FileUtils::MakeMagic32('g', 'l', 'T', 'F'); + constexpr uint32_t GLTF_VERSION = 2u; + + constexpr uint32_t CHUNK_MAGIC_JSON = FileUtils::MakeMagic32('J', 'S', 'O', 'N'); + constexpr uint32_t CHUNK_MAGIC_BIN = FileUtils::MakeMagic32('B', 'I', 'N', '\x00'); + + constexpr auto GLTF_LENGTH_OFFSET = 8u; + constexpr auto GLTF_JSON_CHUNK_LENGTH_OFFSET = 12u; + constexpr auto GLTF_JSON_CHUNK_DATA_OFFSET = 20u; + + constexpr auto GLTF_DATA_URI_PREFIX = "data:application/octet-stream;base64,"; +} // namespace gltf diff --git a/src/ObjWriting.lua b/src/ObjWriting.lua index 8505cd8b..fa796ae8 100644 --- a/src/ObjWriting.lua +++ b/src/ObjWriting.lua @@ -19,6 +19,7 @@ function ObjWriting:link(links) links:linkto(ZoneCommon) links:linkto(minilzo) links:linkto(minizip) + links:linkto(libtomcrypt) end function ObjWriting:use() @@ -55,5 +56,6 @@ function ObjWriting:project() minilzo:include(includes) minizip:include(includes) json:include(includes) + libtomcrypt:include(includes) end diff --git a/src/ObjWriting/XModel/Gltf/GltfBinOutput.cpp b/src/ObjWriting/XModel/Gltf/GltfBinOutput.cpp new file mode 100644 index 00000000..7bce3561 --- /dev/null +++ b/src/ObjWriting/XModel/Gltf/GltfBinOutput.cpp @@ -0,0 +1,86 @@ +#include "GltfBinOutput.h" + +#include "Utils/Alignment.h" +#include "XModel/Gltf/GltfConstants.h" + +#include +#include + +using namespace gltf; + +BinOutput::BinOutput(std::ostream& stream) + : m_stream(stream) +{ +} + +void BinOutput::Write(const void* data, const size_t dataSize) const +{ + m_stream.write(static_cast(data), dataSize); +} + +void BinOutput::AlignToFour(const char value) const +{ + const auto offset = m_stream.tellp(); + if (offset % 4 > 0) + { + const uint32_t alignmentValue = FileUtils::MakeMagic32(value, value, value, value); + Write(&alignmentValue, 4u - (offset % 4u)); + } +} + +std::optional BinOutput::CreateBufferUri(const void* buffer, size_t bufferSize) const +{ + return std::nullopt; +} + +void BinOutput::EmitJson(const nlohmann::json& json) const +{ + static constexpr uint32_t ZERO = 0u; + + Write(&GLTF_MAGIC, sizeof(GLTF_MAGIC)); + Write(&GLTF_VERSION, sizeof(GLTF_VERSION)); + + // File length will be filled later + Write(&ZERO, sizeof(ZERO)); + + // Chunk length will be filled after writing json + Write(&ZERO, sizeof(ZERO)); + Write(&CHUNK_MAGIC_JSON, sizeof(CHUNK_MAGIC_JSON)); + + m_stream << json; + + AlignToFour(' '); + + const auto offsetAfterData = m_stream.tellp(); + const auto jsonDataLength = static_cast(static_cast(offsetAfterData) - GLTF_JSON_CHUNK_DATA_OFFSET); + + // Fill chunk length now + m_stream.seekp(GLTF_JSON_CHUNK_LENGTH_OFFSET, std::ios::beg); + Write(&jsonDataLength, sizeof(jsonDataLength)); + + // Return to previous pos + m_stream.seekp(offsetAfterData, std::ios::beg); +} + +void BinOutput::EmitBuffer(const void* buffer, const size_t bufferSize) const +{ + const auto chunkLength = utils::Align(bufferSize, 4u); + Write(&chunkLength, sizeof(chunkLength)); + Write(&CHUNK_MAGIC_BIN, sizeof(CHUNK_MAGIC_BIN)); + + Write(buffer, bufferSize); + AlignToFour('\0'); +} + +void BinOutput::Finalize() const +{ + const auto fileEndOffset = m_stream.tellp(); + const auto fileSize = static_cast(fileEndOffset); + + // Fill chunk length now + m_stream.seekp(GLTF_LENGTH_OFFSET, std::ios::beg); + Write(&fileSize, sizeof(fileSize)); + + // Return to file end + m_stream.seekp(fileEndOffset, std::ios::beg); +} diff --git a/src/ObjWriting/XModel/Gltf/GltfBinOutput.h b/src/ObjWriting/XModel/Gltf/GltfBinOutput.h new file mode 100644 index 00000000..91eacc75 --- /dev/null +++ b/src/ObjWriting/XModel/Gltf/GltfBinOutput.h @@ -0,0 +1,25 @@ +#pragma once + +#include "GltfOutput.h" + +#include + +namespace gltf +{ + class BinOutput final : public Output + { + public: + explicit BinOutput(std::ostream& stream); + + std::optional CreateBufferUri(const void* buffer, size_t bufferSize) const override; + void EmitJson(const nlohmann::json& json) const override; + void EmitBuffer(const void* buffer, size_t bufferSize) const override; + void Finalize() const override; + + private: + void Write(const void* data, size_t dataSize) const; + void AlignToFour(char value) const; + + std::ostream& m_stream; + }; +} // namespace gltf diff --git a/src/ObjWriting/XModel/Gltf/GltfOutput.h b/src/ObjWriting/XModel/Gltf/GltfOutput.h new file mode 100644 index 00000000..8cc7a3da --- /dev/null +++ b/src/ObjWriting/XModel/Gltf/GltfOutput.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include + +namespace gltf +{ + class Output + { + protected: + Output() = default; + virtual ~Output() = default; + Output(const Output& other) = default; + Output(Output&& other) noexcept = default; + Output& operator=(const Output& other) = default; + Output& operator=(Output&& other) noexcept = default; + + public: + virtual std::optional CreateBufferUri(const void* buffer, size_t bufferSize) const = 0; + virtual void EmitJson(const nlohmann::json& json) const = 0; + virtual void EmitBuffer(const void* buffer, size_t bufferSize) const = 0; + virtual void Finalize() const = 0; + }; +} // namespace gltf diff --git a/src/ObjWriting/XModel/Gltf/GltfTextOutput.cpp b/src/ObjWriting/XModel/Gltf/GltfTextOutput.cpp new file mode 100644 index 00000000..7547b3d2 --- /dev/null +++ b/src/ObjWriting/XModel/Gltf/GltfTextOutput.cpp @@ -0,0 +1,48 @@ +#include "GltfTextOutput.h" + +#include "Utils/Alignment.h" +#include "XModel/Gltf/GltfConstants.h" + +#include +#include +#include + +using namespace gltf; + +TextOutput::TextOutput(std::ostream& stream) + : m_stream(stream) +{ +} + +std::optional TextOutput::CreateBufferUri(const void* buffer, const size_t bufferSize) const +{ + static constexpr auto URI_PREFIX_LENGTH = std::char_traits::length(GLTF_DATA_URI_PREFIX); + const auto base64Length = utils::Align(4u * (bufferSize / 3u), 4u); + const auto base64BufferSize = URI_PREFIX_LENGTH + base64Length; + + std::string output(base64BufferSize, '\0'); + + std::memcpy(output.data(), &GLTF_DATA_URI_PREFIX, URI_PREFIX_LENGTH); + + unsigned long outLength = base64Length; + base64_encode(static_cast(buffer), bufferSize, &output[URI_PREFIX_LENGTH], &outLength); + + assert(outLength == base64Length); + + return output; +} + +void TextOutput::EmitJson(const nlohmann::json& json) const +{ + m_stream << std::setw(4) << json; +} + +void TextOutput::EmitBuffer(const void* buffer, const size_t bufferSize) const +{ + // Nothing to do +} + +void TextOutput::Finalize() const +{ + // Nothing to do +} diff --git a/src/ObjWriting/XModel/Gltf/GltfTextOutput.h b/src/ObjWriting/XModel/Gltf/GltfTextOutput.h new file mode 100644 index 00000000..d6b9d7b9 --- /dev/null +++ b/src/ObjWriting/XModel/Gltf/GltfTextOutput.h @@ -0,0 +1,22 @@ +#pragma once + +#include "GltfOutput.h" + +#include + +namespace gltf +{ + class TextOutput final : public Output + { + public: + explicit TextOutput(std::ostream& stream); + + std::optional CreateBufferUri(const void* buffer, size_t bufferSize) const override; + void EmitJson(const nlohmann::json& json) const override; + void EmitBuffer(const void* buffer, size_t bufferSize) const override; + void Finalize() const override; + + private: + std::ostream& m_stream; + }; +} // namespace gltf