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

Add support for point clouds (pnts) #591

Merged
merged 21 commits into from
Feb 6, 2023
Merged
Show file tree
Hide file tree
Changes from 18 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
6 changes: 6 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Change Log

### v0.?.? - 2023-?-?

##### Additions :tada:

- Added support for loading tilesets with `pnts` content. Point clouds are converted to `glTF`s with a single `POINTS` primitive, while batch tables are converted to `EXT_feature_metadata`.

### v0.21.3 - 2023-02-01

##### Fixes :wrench:
Expand Down
1 change: 1 addition & 0 deletions Cesium3DTilesSelection/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ target_link_libraries(Cesium3DTilesSelection
uriparser
libmorton
expected-lite
${CESIUM_NATIVE_DRACO_LIBRARY}
)

install(TARGETS Cesium3DTilesSelection
Expand Down
2 changes: 1 addition & 1 deletion Cesium3DTilesSelection/src/B3dmToGltfConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ void convertB3dmMetadataToGltfFeatureMetadata(
}

// upgrade batch table to glTF feature metadata and append the result
result.errors.merge(BatchTableToGltfFeatureMetadata::convert(
result.errors.merge(BatchTableToGltfFeatureMetadata::convertFromB3dm(
featureTableJson,
batchTableJson,
batchTableBinaryData,
Expand Down
173 changes: 133 additions & 40 deletions Cesium3DTilesSelection/src/BatchTableToGltfFeatureMetadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ struct CompatibleTypes {
};

struct BinaryProperty {
int64_t b3dmByteOffset;
int64_t batchTableByteOffset;
int64_t gltfByteOffset;
int64_t byteLength;
};
Expand All @@ -55,8 +55,8 @@ struct GltfFeatureTableType {
size_t typeSize;
};

const std::map<std::string, GltfFeatureTableType> b3dmComponentTypeToGltfType =
{
const std::map<std::string, GltfFeatureTableType>
batchTableComponentTypeToGltfType = {
{"BYTE", GltfFeatureTableType{"INT8", sizeof(int8_t)}},
{"UNSIGNED_BYTE", GltfFeatureTableType{"UINT8", sizeof(uint8_t)}},
{"SHORT", GltfFeatureTableType{"INT16", sizeof(int16_t)}},
Expand Down Expand Up @@ -1223,13 +1223,13 @@ void updateExtensionWithBinaryProperty(
assert(
gltfBufferIndex >= 0 &&
"gltfBufferIndex is negative. Need to allocate buffer before "
"convert the binary property");
"converting the binary property");

const auto& byteOffsetIt = propertyValue.FindMember("byteOffset");
if (byteOffsetIt == propertyValue.MemberEnd() ||
!byteOffsetIt->value.IsInt64()) {
result.emplaceWarning(fmt::format(
"Skip convert {}. The binary property doesn't have required "
"Skip converting {}. The binary property doesn't have required "
"byteOffset.",
propertyName));
return;
Expand All @@ -1239,7 +1239,7 @@ void updateExtensionWithBinaryProperty(
if (componentTypeIt == propertyValue.MemberEnd() ||
!componentTypeIt->value.IsString()) {
result.emplaceWarning(fmt::format(
"Skip convert {}. The binary property doesn't have required "
"Skip converting {}. The binary property doesn't have required "
"componentType.",
propertyName));
return;
Expand All @@ -1258,8 +1258,8 @@ void updateExtensionWithBinaryProperty(
const std::string& componentType = componentTypeIt->value.GetString();
const std::string& type = typeIt->value.GetString();

auto convertedTypeIt = b3dmComponentTypeToGltfType.find(componentType);
if (convertedTypeIt == b3dmComponentTypeToGltfType.end()) {
auto convertedTypeIt = batchTableComponentTypeToGltfType.find(componentType);
if (convertedTypeIt == batchTableComponentTypeToGltfType.end()) {
return;
}
const GltfFeatureTableType& gltfType = convertedTypeIt->second;
Expand Down Expand Up @@ -1297,7 +1297,7 @@ void updateExtensionWithBinaryProperty(
featureTableProperty.bufferView =
static_cast<int32_t>(gltf.bufferViews.size() - 1);

binaryProperty.b3dmByteOffset = byteOffset;
binaryProperty.batchTableByteOffset = byteOffset;
binaryProperty.gltfByteOffset = gltfBufferOffset;
binaryProperty.byteLength = static_cast<int64_t>(bufferView.byteLength);
}
Expand Down Expand Up @@ -1379,37 +1379,12 @@ void updateExtensionWithBatchTableHierarchy(
}
}

} // namespace

ErrorList BatchTableToGltfFeatureMetadata::convert(
const rapidjson::Document& featureTableJson,
void convertBatchTableToGltfFeatureMetadataExtension(
const rapidjson::Document& batchTableJson,
const gsl::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 b3dm batch table and convert it to the EXT_feature_metadata
// extension.

// If the feature table is missing the BATCH_LENGTH semantic, ignore the batch
// table completely.
const auto batchLengthIt = featureTableJson.FindMember("BATCH_LENGTH");
if (batchLengthIt == featureTableJson.MemberEnd() ||
!batchLengthIt->value.IsInt64()) {
result.emplaceWarning(
"The B3DM has a batch table, but it is being ignored because there is "
"no BATCH_LENGTH semantic in the feature table or it is not an "
"integer.");
return result;
}

const int64_t batchLength = batchLengthIt->value.GetInt64();

CesiumGltf::Model& gltf,
const int64_t featureCount,
ErrorList& result) {
// Add the binary part of the batch table - if any - to the glTF as a buffer.
// We will reallign this buffer later on
int32_t gltfBufferIndex = -1;
Expand All @@ -1430,7 +1405,7 @@ ErrorList BatchTableToGltfFeatureMetadata::convert(
FeatureTable& featureTable =
modelExtension.featureTables.emplace("default", FeatureTable())
.first->second;
featureTable.count = batchLength;
featureTable.count = featureCount;
featureTable.classProperty = "default";

// Convert each regular property in the batch table
Expand Down Expand Up @@ -1499,10 +1474,49 @@ ErrorList BatchTableToGltfFeatureMetadata::convert(
for (const BinaryProperty& binaryProperty : binaryProperties) {
std::memcpy(
buffer.cesium.data.data() + binaryProperty.gltfByteOffset,
batchTableBinaryData.data() + binaryProperty.b3dmByteOffset,
batchTableBinaryData.data() + binaryProperty.batchTableByteOffset,
static_cast<size_t>(binaryProperty.byteLength));
}
}
}

} // namespace

ErrorList BatchTableToGltfFeatureMetadata::convertFromB3dm(
const rapidjson::Document& featureTableJson,
const rapidjson::Document& batchTableJson,
const gsl::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 b3dm batch table and convert it to the EXT_feature_metadata
// extension.

// If the feature table is missing the BATCH_LENGTH semantic, ignore the batch
// table completely.
const auto batchLengthIt = featureTableJson.FindMember("BATCH_LENGTH");
if (batchLengthIt == featureTableJson.MemberEnd() ||
!batchLengthIt->value.IsInt64()) {
result.emplaceWarning(
"The B3DM has a batch table, but it is being ignored because there is "
"no BATCH_LENGTH semantic in the feature table or it is not an "
"integer.");
return result;
}

const int64_t batchLength = batchLengthIt->value.GetInt64();

convertBatchTableToGltfFeatureMetadataExtension(
batchTableJson,
batchTableBinaryData,
gltf,
batchLength,
result);

// Create an EXT_feature_metadata extension for each primitive with a _BATCHID
// attribute.
Expand Down Expand Up @@ -1530,4 +1544,83 @@ ErrorList BatchTableToGltfFeatureMetadata::convert(

return result;
}

ErrorList BatchTableToGltfFeatureMetadata::convertFromPnts(
const rapidjson::Document& featureTableJson,
const rapidjson::Document& batchTableJson,
const gsl::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 pnts batch table and convert it to the EXT_feature_metadata
// extension.

const auto pointsLengthIt = featureTableJson.FindMember("POINTS_LENGTH");
if (pointsLengthIt == featureTableJson.MemberEnd() ||
!pointsLengthIt->value.IsInt64()) {
result.emplaceError("The PNTS cannot be parsed because there is no valid "
"POINTS_LENGTH semantic.");
return result;
}

int64_t featureCount = 0;
const auto batchLengthIt = featureTableJson.FindMember("BATCH_LENGTH");
const auto batchIdIt = featureTableJson.FindMember("BATCH_ID");

// If the feature table is missing the BATCH_LENGTH semantic, the batch table
// corresponds to per-point properties.
if (batchLengthIt != featureTableJson.MemberEnd() &&
batchLengthIt->value.IsInt64()) {
featureCount = batchLengthIt->value.GetInt64();
} else if (
batchIdIt != featureTableJson.MemberEnd() &&
batchIdIt->value.IsObject()) {
result.emplaceWarning(
"The PNTS has a batch table, but it is being ignored because there "
"is no valid BATCH_LENGTH in the feature table even though BATCH_ID is "
"defined.");
return result;
} else {
featureCount = pointsLengthIt->value.GetInt64();
}

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

// Create the EXT_feature_metadata extension for the single mesh primitive.
assert(gltf.meshes.size() == 1);
Mesh& mesh = gltf.meshes[0];

assert(mesh.primitives.size() == 1);
MeshPrimitive& primitive = mesh.primitives[0];

ExtensionMeshPrimitiveExtFeatureMetadata& extension =
primitive.addExtension<ExtensionMeshPrimitiveExtFeatureMetadata>();
FeatureIDAttribute& attribute = extension.featureIdAttributes.emplace_back();
attribute.featureTable = "default";

auto primitiveBatchIdIt = primitive.attributes.find("_BATCHID");
if (primitiveBatchIdIt != primitive.attributes.end()) {
// If _BATCHID is present, rename the _BATCHID attribute to _FEATURE_ID_0
primitive.attributes["_FEATURE_ID_0"] = primitiveBatchIdIt->second;
primitive.attributes.erase("_BATCHID");
attribute.featureIds.attribute = "_FEATURE_ID_0";
} else {
// Otherwise, use implicit feature IDs to indicate the metadata is stored in
// per-point properties.
attribute.featureIds.constant = 0;
attribute.featureIds.divisor = 1;
}

return result;
}
} // namespace Cesium3DTilesSelection
8 changes: 7 additions & 1 deletion Cesium3DTilesSelection/src/BatchTableToGltfFeatureMetadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@

namespace Cesium3DTilesSelection {
struct BatchTableToGltfFeatureMetadata {
static ErrorList convert(
static ErrorList convertFromB3dm(
const rapidjson::Document& featureTableJson,
const rapidjson::Document& batchTableJson,
const gsl::span<const std::byte>& batchTableBinaryData,
CesiumGltf::Model& gltf);

static ErrorList convertFromPnts(
const rapidjson::Document& featureTableJson,
const rapidjson::Document& batchTableJson,
const gsl::span<const std::byte>& batchTableBinaryData,
Expand Down
Loading