Skip to content

Commit

Permalink
Merge pull request #591 from CesiumGS/pnts-support
Browse files Browse the repository at this point in the history
Add support for point clouds (pnts)
  • Loading branch information
kring committed Feb 6, 2023
2 parents f5f1bae + 547f922 commit 7600488
Show file tree
Hide file tree
Showing 27 changed files with 3,702 additions and 79 deletions.
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

0 comments on commit 7600488

Please sign in to comment.