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

Instance feature metadata #1537

Merged
merged 12 commits into from
Dec 20, 2024
32 changes: 25 additions & 7 deletions Source/CesiumRuntime/Private/CesiumFeatureIdAttribute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,6 @@ FCesiumFeatureIdAttribute::FCesiumFeatureIdAttribute(
return;
}

const CesiumGltf::Accessor* accessor =
Model.getSafe<CesiumGltf::Accessor>(&Model.accessors, featureID->second);
if (!accessor || accessor->type != CesiumGltf::Accessor::Type::SCALAR) {
this->_status = ECesiumFeatureIdAttributeStatus::ErrorInvalidAccessor;
return;
}

this->_featureIdAccessor = CesiumGltf::getFeatureIdAccessorView(
Model,
Primitive,
Expand All @@ -44,6 +37,31 @@ FCesiumFeatureIdAttribute::FCesiumFeatureIdAttribute(
this->_featureIdAccessor);
}

FCesiumFeatureIdAttribute::FCesiumFeatureIdAttribute(
const CesiumGltf::Model& Model,
const CesiumGltf::Node& Node,
const int64 FeatureIDAttribute,
const FString& PropertyTableName)
: _status(ECesiumFeatureIdAttributeStatus::ErrorInvalidAttribute),
_featureIdAccessor(),
_attributeIndex(FeatureIDAttribute),
_propertyTableName(PropertyTableName) {
this->_featureIdAccessor = CesiumGltf::getFeatureIdAccessorView(
Model,
Node,
static_cast<int32_t>(this->_attributeIndex));

this->_status = std::visit(
[](auto&& view) {
if (view.status() != CesiumGltf::AccessorViewStatus::Valid) {
return ECesiumFeatureIdAttributeStatus::ErrorInvalidAccessor;
}

return ECesiumFeatureIdAttributeStatus::Valid;
},
this->_featureIdAccessor);
}

const FString& UCesiumFeatureIdAttributeBlueprintLibrary::GetFeatureTableName(
UPARAM(ref) const FCesiumFeatureIdAttribute& FeatureIDAttribute) {
return FeatureIDAttribute._propertyTableName;
Expand Down
71 changes: 70 additions & 1 deletion Source/CesiumRuntime/Private/CesiumFeatureIdSet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "CesiumFeatureIdSet.h"
#include "CesiumGltf/Accessor.h"
#include "CesiumGltf/ExtensionExtInstanceFeaturesFeatureId.h"
#include "CesiumGltf/ExtensionModelExtStructuralMetadata.h"
#include "CesiumGltf/FeatureId.h"
#include "CesiumGltf/Model.h"
Expand Down Expand Up @@ -62,6 +63,42 @@ FCesiumFeatureIdSet::FCesiumFeatureIdSet(
}
}

FCesiumFeatureIdSet::FCesiumFeatureIdSet(
const CesiumGltf::Model& InModel,
const CesiumGltf::Node& Node,
const CesiumGltf::ExtensionExtInstanceFeaturesFeatureId& InstanceFeatureID)
: _featureID(),
_featureIDSetType(ECesiumFeatureIdSetType::Instance),
_featureCount(InstanceFeatureID.featureCount),
_nullFeatureID(InstanceFeatureID.nullFeatureId.value_or(-1)),
_propertyTableIndex(InstanceFeatureID.propertyTable.value_or(-1)),
_label(FString(InstanceFeatureID.label.value_or("").c_str())) {
FString propertyTableName;

// For backwards compatibility with GetFeatureTableName.
const CesiumGltf::ExtensionModelExtStructuralMetadata* pMetadata =
InModel.getExtension<CesiumGltf::ExtensionModelExtStructuralMetadata>();
if (pMetadata && this->_propertyTableIndex >= 0) {
size_t index = static_cast<size_t>(_propertyTableIndex);
if (index < pMetadata->propertyTables.size()) {
const CesiumGltf::PropertyTable& propertyTable =
pMetadata->propertyTables[index];
std::string name = propertyTable.name.value_or("");
propertyTableName = FString(name.c_str());
}
}

if (InstanceFeatureID.attribute) {
_featureID = FCesiumFeatureIdAttribute(
InModel,
Node,
*InstanceFeatureID.attribute,
propertyTableName);
} else if (_featureCount > 0) {
_featureIDSetType = ECesiumFeatureIdSetType::InstanceImplicit;
}
}

const ECesiumFeatureIdSetType
UCesiumFeatureIdSetBlueprintLibrary::GetFeatureIDSetType(
UPARAM(ref) const FCesiumFeatureIdSet& FeatureIDSet) {
Expand All @@ -71,7 +108,8 @@ UCesiumFeatureIdSetBlueprintLibrary::GetFeatureIDSetType(
const FCesiumFeatureIdAttribute&
UCesiumFeatureIdSetBlueprintLibrary::GetAsFeatureIDAttribute(
UPARAM(ref) const FCesiumFeatureIdSet& FeatureIDSet) {
if (FeatureIDSet._featureIDSetType == ECesiumFeatureIdSetType::Attribute) {
if (FeatureIDSet._featureIDSetType == ECesiumFeatureIdSetType::Attribute ||
FeatureIDSet._featureIDSetType == ECesiumFeatureIdSetType::Instance) {
return std::get<FCesiumFeatureIdAttribute>(FeatureIDSet._featureID);
}

Expand Down Expand Up @@ -136,9 +174,40 @@ int64 UCesiumFeatureIdSetBlueprintLibrary::GetFeatureIDForVertex(
return -1;
}

int64 UCesiumFeatureIdSetBlueprintLibrary::GetFeatureIDForInstance(
UPARAM(ref) const FCesiumFeatureIdSet& FeatureIDSet,
int64 InstanceIndex) {
ECesiumFeatureIdSetType type = FeatureIDSet._featureIDSetType;
if (type == ECesiumFeatureIdSetType::InstanceImplicit) {
return InstanceIndex;
} else if (
type != ECesiumFeatureIdSetType::Instance ||
!std::holds_alternative<FCesiumFeatureIdAttribute>(
FeatureIDSet._featureID) ||
InstanceIndex < 0) {
return -1;
}
const auto& featureIdAttribute =
std::get<FCesiumFeatureIdAttribute>(FeatureIDSet._featureID);
return UCesiumFeatureIdAttributeBlueprintLibrary::GetFeatureIDForVertex(
featureIdAttribute,
InstanceIndex);
}

int64 UCesiumFeatureIdSetBlueprintLibrary::GetFeatureIDFromHit(
UPARAM(ref) const FCesiumFeatureIdSet& FeatureIDSet,
const FHitResult& Hit) {
// FeatureIDs from instanced geometry take precedence.
const auto* pInstancedComponent =
Cast<UCesiumGltfInstancedComponent>(Hit.Component);
if (IsValid(pInstancedComponent)) {
const FCesiumPrimitiveFeatures& instanceFeatures =
*pInstancedComponent->pInstanceFeatures;
return UCesiumPrimitiveFeaturesBlueprintLibrary::GetFeatureIDFromInstance(
instanceFeatures,
Hit.Item);
}

if (FeatureIDSet._featureIDSetType == ECesiumFeatureIdSetType::Texture) {
FCesiumFeatureIdTexture texture =
std::get<FCesiumFeatureIdTexture>(FeatureIDSet._featureID);
Expand Down
23 changes: 19 additions & 4 deletions Source/CesiumRuntime/Private/CesiumGltfComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include <CesiumGeometry/Transforms.h>
#include <CesiumGltf/AccessorUtility.h>
#include <CesiumGltf/AccessorView.h>
#include <CesiumGltf/ExtensionExtInstanceFeatures.h>
#include <CesiumGltf/ExtensionExtMeshFeatures.h>
#include <CesiumGltf/ExtensionExtMeshGpuInstancing.h>
#include <CesiumGltf/ExtensionKhrMaterialsUnlit.h>
Expand Down Expand Up @@ -1913,8 +1914,10 @@ inline constexpr bool is_int_quat_v = is_int_quat<T>::value;

static void loadInstancingData(
const CesiumGltf::Model& model,
const CesiumGltf::Node& node,
LoadNodeResult& result,
const CesiumGltf::ExtensionExtMeshGpuInstancing* pGpuInstancing) {
const CesiumGltf::ExtensionExtMeshGpuInstancing* pGpuInstancing,
const CesiumGltf::ExtensionExtInstanceFeatures* pInstanceFeatures) {
auto getInstanceAccessor =
[&](const char* name) -> const CesiumGltf::Accessor* {
if (auto accessorItr = pGpuInstancing->attributes.find(name);
Expand Down Expand Up @@ -2034,6 +2037,10 @@ static void loadInstancingData(
auto unrealFMatrix = VecMath::createMatrix(unrealMat);
result.InstanceTransforms[i].SetFromMatrix(unrealFMatrix);
}
if (pInstanceFeatures) {
result.pInstanceFeatures =
MakeShared<FCesiumPrimitiveFeatures>(model, node, *pInstanceFeatures);
}
}

static void loadNode(
Expand Down Expand Up @@ -2117,7 +2124,12 @@ static void loadNode(
if (meshId >= 0 && meshId < model.meshes.size()) {
if (const auto* pGpuInstancingExtension =
node.getExtension<CesiumGltf::ExtensionExtMeshGpuInstancing>()) {
loadInstancingData(model, result, pGpuInstancingExtension);
loadInstancingData(
model,
node,
result,
pGpuInstancingExtension,
node.getExtension<CesiumGltf::ExtensionExtInstanceFeatures>());
}
CreateMeshOptions meshOptions = {&options, &result, meshId};
loadMesh(result.meshResult, nodeTransform, meshOptions, ellipsoid);
Expand Down Expand Up @@ -2879,7 +2891,8 @@ static void loadPrimitiveGameThreadPart(
const Cesium3DTilesSelection::Tile& tile,
bool createNavCollision,
ACesium3DTileset* pTilesetActor,
const std::vector<FTransform>& instanceTransforms) {
const std::vector<FTransform>& instanceTransforms,
const TSharedPtr<FCesiumPrimitiveFeatures>& pInstanceFeatures) {
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::LoadPrimitive)

#if DEBUG_GLTF_ASSET_NAMES
Expand Down Expand Up @@ -2912,6 +2925,7 @@ static void loadPrimitiveGameThreadPart(
for (const FTransform& transform : instanceTransforms) {
pInstancedComponent->AddInstance(transform, false);
}
pInstancedComponent->pInstanceFeatures = pInstanceFeatures;
pCesiumPrimitive = pInstancedComponent;
} else {
auto* pComponent =
Expand Down Expand Up @@ -3270,7 +3284,8 @@ UCesiumGltfComponent::CreateOffGameThread(
tile,
createNavCollision,
pTilesetActor,
node.InstanceTransforms);
node.InstanceTransforms,
node.pInstanceFeatures);
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "Cesium3DTilesSelection/BoundingVolume.h"
#include "CesiumPrimitive.h"
#include "CesiumPrimitiveFeatures.h"
#include "Components/InstancedStaticMeshComponent.h"
#include "Components/StaticMeshComponent.h"
#include "CoreMinimal.h"
Expand Down Expand Up @@ -62,6 +63,8 @@ class UCesiumGltfInstancedComponent : public UInstancedStaticMeshComponent,
CesiumPrimitiveData& getPrimitiveData() override;
const CesiumPrimitiveData& getPrimitiveData() const override;

TSharedPtr<FCesiumPrimitiveFeatures> pInstanceFeatures;

private:
CesiumPrimitiveData _cesiumData;
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include "CesiumGltfPrimitiveComponent.h"
#include "CesiumMetadataValue.h"

#include <optional>

static TMap<FString, FCesiumMetadataValue> EmptyCesiumMetadataValueMap;

TMap<FString, FCesiumMetadataValue>
Expand Down Expand Up @@ -148,23 +150,92 @@ bool UCesiumMetadataPickingBlueprintLibrary::FindUVFromHit(
return true;
}

namespace {
/*
* Returns std:nullopt if the component isn't an instanced static mesh or
* if it doesn't have instance feature IDs. This will prompt
* GetPropertyTableValuesFromHit() to search for feature IDs in the primitive's
* attributes.
*/
std::optional<TMap<FString, FCesiumMetadataValue>>
getInstancePropertyTableValues(
const FHitResult& Hit,
const UCesiumGltfComponent* pModel,
int64 FeatureIDSetIndex) {
const auto* pInstancedComponent =
Cast<UCesiumGltfInstancedComponent>(Hit.Component);
if (!IsValid(pInstancedComponent)) {
return std::nullopt;
}
const TSharedPtr<FCesiumPrimitiveFeatures>& pInstanceFeatures =
pInstancedComponent->pInstanceFeatures;
if (!pInstanceFeatures) {
return std::nullopt;
}

const TArray<FCesiumFeatureIdSet>& featureIDSets =
UCesiumPrimitiveFeaturesBlueprintLibrary::GetFeatureIDSets(
*pInstanceFeatures);
if (FeatureIDSetIndex < 0 || FeatureIDSetIndex >= featureIDSets.Num()) {
return TMap<FString, FCesiumMetadataValue>();
}

const FCesiumFeatureIdSet& featureIDSet = featureIDSets[FeatureIDSetIndex];
const int64 propertyTableIndex =
UCesiumFeatureIdSetBlueprintLibrary::GetPropertyTableIndex(featureIDSet);

const TArray<FCesiumPropertyTable>& propertyTables =
UCesiumModelMetadataBlueprintLibrary::GetPropertyTables(pModel->Metadata);
if (propertyTableIndex < 0 || propertyTableIndex >= propertyTables.Num()) {
return TMap<FString, FCesiumMetadataValue>();
}
const FCesiumPropertyTable& propertyTable =
propertyTables[propertyTableIndex];
int64 featureID =
UCesiumPrimitiveFeaturesBlueprintLibrary::GetFeatureIDFromInstance(
*pInstanceFeatures,
Hit.Item,
FeatureIDSetIndex);
if (featureID < 0) {
return TMap<FString, FCesiumMetadataValue>();
}
return UCesiumPropertyTableBlueprintLibrary::GetMetadataValuesForFeature(
propertyTable,
featureID);
}
} // namespace

TMap<FString, FCesiumMetadataValue>
UCesiumMetadataPickingBlueprintLibrary::GetPropertyTableValuesFromHit(
const FHitResult& Hit,
int64 FeatureIDSetIndex) {
const UCesiumGltfPrimitiveComponent* pGltfComponent =
Cast<UCesiumGltfPrimitiveComponent>(Hit.Component);
if (!IsValid(pGltfComponent)) {
const UCesiumGltfComponent* pModel = nullptr;

if (const auto* pPrimComponent = Cast<UPrimitiveComponent>(Hit.Component);
!IsValid(pPrimComponent)) {
timoore marked this conversation as resolved.
Show resolved Hide resolved
return TMap<FString, FCesiumMetadataValue>();
} else {
pModel = Cast<UCesiumGltfComponent>(pPrimComponent->GetOuter());
}

const UCesiumGltfComponent* pModel =
Cast<UCesiumGltfComponent>(pGltfComponent->GetOuter());
if (!IsValid(pModel)) {
return TMap<FString, FCesiumMetadataValue>();
}

const CesiumPrimitiveData& primData = pGltfComponent->getPrimitiveData();
// Query for instance-level metadata first. (EXT_instance_features)
std::optional<TMap<FString, FCesiumMetadataValue>> maybeProperties =
getInstancePropertyTableValues(Hit, pModel, FeatureIDSetIndex);
if (maybeProperties) {
return *maybeProperties;
}

const auto* pCesiumPrimitive = Cast<ICesiumPrimitive>(Hit.Component);
if (!pCesiumPrimitive) {
return TMap<FString, FCesiumMetadataValue>();
}

// Query for primitive-level metadata. (EXT_mesh_features)
const CesiumPrimitiveData& primData = pCesiumPrimitive->getPrimitiveData();
const FCesiumPrimitiveFeatures& features = primData.Features;
const TArray<FCesiumFeatureIdSet>& featureIDSets =
UCesiumPrimitiveFeaturesBlueprintLibrary::GetFeatureIDSets(features);
Expand Down
29 changes: 29 additions & 0 deletions Source/CesiumRuntime/Private/CesiumPrimitiveFeatures.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Copyright 2020-2024 CesiumGS, Inc. and Contributors

#include "CesiumPrimitiveFeatures.h"
#include "CesiumGltf/ExtensionExtInstanceFeatures.h"
#include "CesiumGltf/ExtensionExtMeshFeatures.h"
#include "CesiumGltf/ExtensionExtMeshGpuInstancing.h"
#include "CesiumGltf/Model.h"
#include "CesiumGltfPrimitiveComponent.h"

Expand All @@ -26,6 +28,19 @@ FCesiumPrimitiveFeatures::FCesiumPrimitiveFeatures(
}
}

FCesiumPrimitiveFeatures::FCesiumPrimitiveFeatures(
const CesiumGltf::Model& Model,
const CesiumGltf::Node& Node,
const CesiumGltf::ExtensionExtInstanceFeatures& InstanceFeatures)
: _vertexCount(0), _primitiveMode(-1) {
if (Node.mesh < 0 || Node.mesh >= Model.meshes.size()) {
return;
}
for (const auto& featureId : InstanceFeatures.featureIds) {
this->_featureIdSets.Emplace(Model, Node, featureId);
}
}

const FCesiumPrimitiveFeatures&
UCesiumPrimitiveFeaturesBlueprintLibrary::GetPrimitiveFeatures(
const UPrimitiveComponent* component) {
Expand Down Expand Up @@ -98,6 +113,20 @@ int64 UCesiumPrimitiveFeaturesBlueprintLibrary::GetFeatureIDFromFace(
FaceIndex));
}

int64 UCesiumPrimitiveFeaturesBlueprintLibrary::GetFeatureIDFromInstance(
const FCesiumPrimitiveFeatures& InstanceFeatures,
int64 InstanceIndex,
int64 FeatureIDSetIndex) {
if (FeatureIDSetIndex < 0 ||
FeatureIDSetIndex >= InstanceFeatures._featureIdSets.Num()) {
return -1;
}
const auto& featureIDSet = InstanceFeatures._featureIdSets[FeatureIDSetIndex];
return UCesiumFeatureIdSetBlueprintLibrary::GetFeatureIDForInstance(
featureIDSet,
InstanceIndex);
}

int64 UCesiumPrimitiveFeaturesBlueprintLibrary::GetFeatureIDFromHit(
UPARAM(ref) const FCesiumPrimitiveFeatures& PrimitiveFeatures,
const FHitResult& Hit,
Expand Down
Loading
Loading