Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

convert I3dm metadata to EXT_structural_metadata and EXT_instance_features #1051

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- Added `DebugTileStateDatabase`, which provides tools for debugging the tile selection algorithm using SQLite.
- Added `CesiumAsync::SqliteHelper`, containing functions for working with SQLite.
- Updates generated classes for `EXT_structural_metadata`. See https://github.com/CesiumGS/glTF/pull/71.
- Added conversion of I3dm batch table metadata to `EXT_structural_metadata` and `EXT_instance_features` extensions.

##### Fixes :wrench:

Expand Down
215 changes: 215 additions & 0 deletions Cesium3DTilesContent/src/BatchTableToGltfStructuralMetadata.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
#include "BatchTableToGltfStructuralMetadata.h"

#include "BatchTableHierarchyPropertyValues.h"
#include "MetadataProperty.h"

#include <Cesium3DTilesContent/GltfConverterUtility.h>
#include <CesiumGltf/ExtensionExtInstanceFeatures.h>
#include <CesiumGltf/ExtensionExtMeshFeatures.h>
#include <CesiumGltf/ExtensionExtMeshGpuInstancing.h>
#include <CesiumGltf/ExtensionKhrDracoMeshCompression.h>
#include <CesiumGltf/ExtensionModelExtStructuralMetadata.h>
#include <CesiumGltf/Model.h>
#include <CesiumGltf/Node.h>
#include <CesiumGltf/PropertyType.h>
#include <CesiumGltf/PropertyTypeTraits.h>
#include <CesiumUtility/Assert.h>
Expand All @@ -14,10 +19,13 @@
#include <glm/glm.hpp>
#include <rapidjson/writer.h>

#include <algorithm>
#include <cstring>
#include <limits>
#include <map>
#include <type_traits>
#include <unordered_set>
#include <variant>

using namespace CesiumGltf;
using namespace Cesium3DTilesContent::CesiumImpl;
Expand Down Expand Up @@ -1976,4 +1984,211 @@ ErrorList BatchTableToGltfStructuralMetadata::convertFromPnts(

return result;
}

// Does something like this already exist?

template <typename T> int32_t componentTypeFromCpp();

template <> int32_t componentTypeFromCpp<uint8_t>() {
return Accessor::ComponentType::UNSIGNED_BYTE;
}

template <> int32_t componentTypeFromCpp<uint16_t>() {
return Accessor::ComponentType::UNSIGNED_SHORT;
}

template <> int32_t componentTypeFromCpp<uint32_t>() {
return Accessor::ComponentType::UNSIGNED_INT;
}

// encapsulation of the binary batch id data in an I3dm
struct BatchIdSemantic {
std::variant<
std::span<const uint8_t>,
std::span<const uint16_t>,
std::span<const uint32_t>>
batchSpan;
const std::byte* rawData;
uint32_t numElements;
uint32_t byteSize;

template <typename UintType>
static std::span<const UintType>
makeSpan(const std::byte* byteData, uint32_t offset, uint32_t numElements) {
return std::span<const UintType>(
reinterpret_cast<const UintType*>(byteData + offset),
numElements);
}

BatchIdSemantic(
const rapidjson::Document& featureTableJson,
uint32_t numInstances,
const std::span<const std::byte>& featureTableJsonData)
: rawData(nullptr), numElements(0), byteSize(0) {
const auto batchIdIt = featureTableJson.FindMember("BATCH_ID");
if (batchIdIt == featureTableJson.MemberEnd() ||
!batchIdIt->value.IsObject()) {
return;
}
const auto byteOffsetIt = batchIdIt->value.FindMember("byteOffset");
if (byteOffsetIt == batchIdIt->value.MemberEnd() ||
!byteOffsetIt->value.IsUint()) {
// Warning
}
uint32_t byteOffset = byteOffsetIt->value.GetUint();
const auto componentTypeIt = batchIdIt->value.FindMember("componentType");
if (componentTypeIt != featureTableJson.MemberEnd() &&
componentTypeIt->value.IsString()) {
const std::string& componentTypeString =
componentTypeIt->value.GetString();
if (MetadataProperty::stringToMetadataComponentType.find(
componentTypeString) ==
MetadataProperty::stringToMetadataComponentType.end()) {
// Warning
}
MetadataProperty::ComponentType componentType =
MetadataProperty::stringToMetadataComponentType.at(
componentTypeString);
rawData = featureTableJsonData.data();
if (componentType == MetadataProperty::ComponentType::UNSIGNED_BYTE) {
batchSpan = makeSpan<uint8_t>(rawData, byteOffset, numInstances);
numElements = numInstances;
byteSize = numElements * sizeof(uint8_t);
} else if (
componentType == MetadataProperty::ComponentType::UNSIGNED_SHORT) {
batchSpan = makeSpan<uint8_t>(rawData, byteOffset, numInstances);
numElements = numInstances;
byteSize = numElements * sizeof(uint16_t);
} else if (
componentType == MetadataProperty::ComponentType::UNSIGNED_INT) {
batchSpan = makeSpan<uint32_t>(rawData, byteOffset, numInstances);
numElements = numInstances;
byteSize = numElements * sizeof(uint32_t);
}
}
}

size_t idSize() const {
return std::visit(
[](auto&& batchIds) { return sizeof(batchIds[0]); },
batchSpan);
}

uint32_t maxBatchId() const {
return std::visit(
[](auto&& batchIds) {
auto itr = std::ranges::max_element(batchIds);
return static_cast<uint32_t>(*itr);
},
batchSpan);
}

int32_t componentType() const {
return std::visit(
[](auto&& batchIds) {
using span_type = std::remove_reference_t<decltype(batchIds)>;
return componentTypeFromCpp<typename span_type::value_type>();
},
batchSpan);
}
};

// returns an accessor ID for the added feature IDs
int32_t
addFeatureIdsToGltf(CesiumGltf::Model& gltf, const BatchIdSemantic& batchIds) {
int32_t featuresBufferId = GltfConverterUtility::createBufferInGltf(gltf);
auto& featuresBuffer = gltf.buffers[static_cast<uint32_t>(featuresBufferId)];
featuresBuffer.cesium.data.resize(batchIds.byteSize);
std::memcpy(
&featuresBuffer.cesium.data[0],
batchIds.rawData,
batchIds.byteSize);
int32_t featuresBufferViewId = GltfConverterUtility::createBufferViewInGltf(
gltf,
featuresBufferId,
0,
static_cast<int64_t>(batchIds.idSize()));
gltf.bufferViews[static_cast<uint32_t>(featuresBufferViewId)].byteLength =
batchIds.byteSize;

int32_t accessorId = GltfConverterUtility::createAccessorInGltf(
gltf,
featuresBufferViewId,
batchIds.componentType(),
batchIds.numElements,
Accessor::Type::SCALAR);
return accessorId;
}

ErrorList BatchTableToGltfStructuralMetadata::convertFromI3dm(
const rapidjson::Document& featureTableJson,
const rapidjson::Document& batchTableJson,
const std::span<const std::byte>& featureTableJsonData,
const std::span<const std::byte>& batchTableBinaryData,
CesiumGltf::Model& gltf) {
// Check to make sure a char of rapidjson is 1 byte
static_assert(
sizeof(rapidjson::Value::Ch) == 1,
"RapidJson::Value::Ch is not 1 byte");

ErrorList result;

// Parse the batch table and convert it to the EXT_structural_metadata
// extension.

// Batch table length is either the max batch ID + 1 or, if there are no batch
// IDs, the number of instances.
int64_t featureCount = 0;
std::optional<BatchIdSemantic> optBatchIds;
const auto batchIdIt = featureTableJson.FindMember("BATCH_ID");
std::optional<uint32_t> optInstancesLength =
GltfConverterUtility::getValue<uint32_t>(
featureTableJson,
"INSTANCES_LENGTH");
if (!optInstancesLength) {
result.emplaceError("Required INSTANCES_LENGTH semantic is missing");
return result;
}
if (batchIdIt == featureTableJson.MemberEnd()) {
featureCount = *optInstancesLength;
} else {
optBatchIds = BatchIdSemantic(
featureTableJson,
*optInstancesLength,
featureTableJsonData);
uint32_t maxBatchId = optBatchIds->maxBatchId();
featureCount = maxBatchId + 1;
}

convertBatchTableToGltfStructuralMetadataExtension(
batchTableJson,
batchTableBinaryData,
gltf,
featureCount,
result);

int32_t featureIdAccessor = -1;
if (optBatchIds.has_value()) {
featureIdAccessor = addFeatureIdsToGltf(gltf, *optBatchIds);
}

// Create an EXT_instance_features extension for node that has an
// EXT_mesh_gpu_instancing extension
for (Node& node : gltf.nodes) {
if (auto* pGpuInstancing =
node.getExtension<ExtensionExtMeshGpuInstancing>()) {
auto& instanceFeatureExt =
node.addExtension<ExtensionExtInstanceFeatures>();
gltf.addExtensionUsed(ExtensionExtInstanceFeatures::ExtensionName);
instanceFeatureExt.featureIds.resize(1);
instanceFeatureExt.featureIds[0].featureCount = featureCount;
instanceFeatureExt.featureIds[0].propertyTable = 0;
if (featureIdAccessor >= 0) {
pGpuInstancing->attributes["_FEATURE_ID_0"] = featureIdAccessor;
instanceFeatureExt.featureIds[0].attribute = 0;
}
}
}
return result;
}
} // namespace Cesium3DTilesContent
7 changes: 7 additions & 0 deletions Cesium3DTilesContent/src/BatchTableToGltfStructuralMetadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,12 @@ struct BatchTableToGltfStructuralMetadata {
const rapidjson::Document& batchTableJson,
const std::span<const std::byte>& batchTableBinaryData,
CesiumGltf::Model& gltf);

static CesiumUtility::ErrorList convertFromI3dm(
const rapidjson::Document& featureTableJson,
const rapidjson::Document& batchTableJson,
const std::span<const std::byte>& featureTableJsonData,
const std::span<const std::byte>& batchTableBinaryData,
CesiumGltf::Model& gltf);
};
} // namespace Cesium3DTilesContent
Loading