cesium-native/CesiumGltfWriter/src/GltfWriter.cpp

194 lines
5.4 KiB
C++

#include "CesiumGltfWriter/GltfWriter.h"
#include "ModelJsonWriter.h"
#include "registerWriterExtensions.h"
#include <CesiumJsonWriter/JsonWriter.h>
#include <CesiumJsonWriter/PrettyJsonWriter.h>
#include <CesiumUtility/Assert.h>
#include <CesiumUtility/Tracing.h>
namespace CesiumGltfWriter {
namespace {
[[nodiscard]] size_t
getPadding(size_t byteCount, size_t byteAlignment) noexcept {
CESIUM_ASSERT(byteAlignment > 0);
size_t remainder = byteCount % byteAlignment;
size_t padding = remainder == 0 ? 0 : byteAlignment - remainder;
return padding;
}
void writeGlbBuffer(
GltfWriterResult& result,
const gsl::span<const std::byte>& jsonData,
const gsl::span<const std::byte>& bufferData,
size_t binaryChunkByteAlignment) {
CESIUM_ASSERT(
binaryChunkByteAlignment > 0 && binaryChunkByteAlignment % 4 == 0);
size_t headerSize = 12;
size_t chunkHeaderSize = 8;
size_t jsonPaddingSize =
getPadding(headerSize + chunkHeaderSize + jsonData.size(), 4);
size_t jsonChunkDataSize = jsonData.size() + jsonPaddingSize;
size_t glbSize = headerSize + chunkHeaderSize + jsonChunkDataSize;
size_t binaryPaddingSize = 0;
size_t binaryChunkDataSize = 0;
if (bufferData.size() > 0) {
size_t extraJsonPadding =
getPadding(glbSize + chunkHeaderSize, binaryChunkByteAlignment);
if (extraJsonPadding > 0) {
jsonPaddingSize += extraJsonPadding;
jsonChunkDataSize += extraJsonPadding;
glbSize += extraJsonPadding;
}
binaryPaddingSize =
getPadding(glbSize + chunkHeaderSize + bufferData.size(), 4);
binaryChunkDataSize = bufferData.size() + binaryPaddingSize;
glbSize += chunkHeaderSize + binaryChunkDataSize;
}
// GLB stores its own length as a uint32. So if that would be >= 4GB , we
// can't output a valid GLB.
if (glbSize > size_t(std::numeric_limits<uint32_t>::max())) {
result.errors.emplace_back(
"glTF is too large to represent as a binary glTF (GLB). The total size "
"of the GLB must be less than 4GB.");
return;
}
std::vector<std::byte>& glb = result.gltfBytes;
glb.resize(glbSize);
uint8_t* glb8 = reinterpret_cast<uint8_t*>(glb.data());
uint32_t* glb32 = reinterpret_cast<uint32_t*>(glb.data());
// GLB header
size_t byteOffset = 0;
glb8[byteOffset++] = 'g';
glb8[byteOffset++] = 'l';
glb8[byteOffset++] = 'T';
glb8[byteOffset++] = 'F';
glb32[byteOffset / 4] = 2;
byteOffset += 4;
glb32[byteOffset / 4] = static_cast<uint32_t>(glbSize);
byteOffset += 4;
// JSON chunk header
glb32[byteOffset / 4] = static_cast<uint32_t>(jsonChunkDataSize);
byteOffset += 4;
glb8[byteOffset++] = 'J';
glb8[byteOffset++] = 'S';
glb8[byteOffset++] = 'O';
glb8[byteOffset++] = 'N';
// JSON chunk
memcpy(glb8 + byteOffset, jsonData.data(), jsonData.size());
byteOffset += jsonData.size();
// JSON chunk padding
memset(glb8 + byteOffset, ' ', jsonPaddingSize);
byteOffset += jsonPaddingSize;
if (bufferData.size() > 0) {
// Binary chunk header
glb32[byteOffset / 4] = static_cast<uint32_t>(binaryChunkDataSize);
byteOffset += 4;
glb8[byteOffset++] = 'B';
glb8[byteOffset++] = 'I';
glb8[byteOffset++] = 'N';
glb8[byteOffset++] = 0;
// Binary chunk
memcpy(glb8 + byteOffset, bufferData.data(), bufferData.size());
byteOffset += bufferData.size();
// Binary chunk padding
memset(glb8 + byteOffset, 0, binaryPaddingSize);
}
}
} // namespace
GltfWriter::GltfWriter() { registerWriterExtensions(this->_context); }
CesiumJsonWriter::ExtensionWriterContext& GltfWriter::getExtensions() {
return this->_context;
}
const CesiumJsonWriter::ExtensionWriterContext&
GltfWriter::getExtensions() const {
return this->_context;
}
GltfWriterResult GltfWriter::writeGltf(
const CesiumGltf::Model& model,
const GltfWriterOptions& options) const {
CESIUM_TRACE("GltfWriter::writeGltf");
const CesiumJsonWriter::ExtensionWriterContext& context =
this->getExtensions();
GltfWriterResult result;
std::unique_ptr<CesiumJsonWriter::JsonWriter> writer;
if (options.prettyPrint) {
writer = std::make_unique<CesiumJsonWriter::PrettyJsonWriter>();
} else {
writer = std::make_unique<CesiumJsonWriter::JsonWriter>();
}
ModelJsonWriter::write(model, *writer, context);
result.gltfBytes = writer->toBytes();
result.errors = writer->getErrors();
result.warnings = writer->getWarnings();
return result;
}
GltfWriterResult GltfWriter::writeGlb(
const CesiumGltf::Model& model,
const gsl::span<const std::byte>& bufferData,
const GltfWriterOptions& options) const {
CESIUM_TRACE("GltfWriter::writeGlb");
const CesiumJsonWriter::ExtensionWriterContext& context =
this->getExtensions();
GltfWriterResult result;
std::unique_ptr<CesiumJsonWriter::JsonWriter> writer;
if (options.prettyPrint) {
writer = std::make_unique<CesiumJsonWriter::PrettyJsonWriter>();
} else {
writer = std::make_unique<CesiumJsonWriter::JsonWriter>();
}
ModelJsonWriter::write(model, *writer, context);
std::vector<std::byte> jsonData = writer->toBytes();
writeGlbBuffer(
result,
gsl::span(jsonData),
bufferData,
options.binaryChunkByteAlignment);
result.errors.insert(
result.errors.end(),
writer->getErrors().begin(),
writer->getErrors().end());
result.warnings.insert(
result.warnings.end(),
writer->getWarnings().begin(),
writer->getWarnings().end());
return result;
}
} // namespace CesiumGltfWriter