diff --git a/Source/CesiumRuntime/Private/Cesium3DTileset.cpp b/Source/CesiumRuntime/Private/Cesium3DTileset.cpp index 47c1a1916..ce331e8e2 100644 --- a/Source/CesiumRuntime/Private/Cesium3DTileset.cpp +++ b/Source/CesiumRuntime/Private/Cesium3DTileset.cpp @@ -463,11 +463,11 @@ void ACesium3DTileset::UpdateTransformFromCesium() { const glm::dmat4& CesiumToUnreal = this->GetCesiumTilesetToUnrealRelativeWorldTransform(); - TArray gltfComponents; - this->GetComponents(gltfComponents); + TInlineComponentArray primitiveComponents( + this); - for (UCesiumGltfComponent* pGltf : gltfComponents) { - pGltf->UpdateTransformFromCesium(CesiumToUnreal); + for (UCesiumGltfPrimitiveComponent* pPrimitive : primitiveComponents) { + pPrimitive->UpdateTransformFromCesium(CesiumToUnreal); } } @@ -512,8 +512,7 @@ void ACesium3DTileset::OnConstruction(const FTransform& Transform) { for (UCesiumGltfComponent* pGltf : gltfComponents) { if (pGltf && IsValid(pGltf) && pGltf->IsVisible()) { - pGltf->SetVisibility(false, true); - pGltf->SetCollisionEnabled(ECollisionEnabled::NoCollision); + pGltf->HideGltf(); } } } @@ -585,7 +584,7 @@ class UnrealResourcePreparer pLoadThreadResult)); return UCesiumGltfComponent::CreateOnGameThread( this->_pActor, - std::move(pHalf), + MoveTemp(pHalf), _pActor->GetCesiumTilesetToUnrealRelativeWorldTransform(), this->_pActor->GetMaterial(), this->_pActor->GetWaterMaterial(), @@ -1395,8 +1394,7 @@ void hideTilesToNoLongerRender( UCesiumGltfComponent* Gltf = static_cast(pTile->getRendererResources()); if (Gltf && Gltf->IsVisible()) { - Gltf->SetVisibility(false, true); - Gltf->SetCollisionEnabled(ECollisionEnabled::NoCollision); + Gltf->HideGltf(); } else { // TODO: why is this happening? UE_LOG( @@ -1417,9 +1415,14 @@ void hideTilesToNoLongerRender( */ void applyActorCollisionSettings( const FBodyInstance& BodyInstance, - UCesiumGltfComponent* Gltf) { + USceneComponent* Component) { + + if (!Component) { + return; + } + UCesiumGltfPrimitiveComponent* PrimitiveComponent = - static_cast(Gltf->GetChildComponent(0)); + Cast(Component); if (PrimitiveComponent != nullptr) { if (PrimitiveComponent->GetCollisionObjectType() != BodyInstance.GetObjectType()) { @@ -1432,6 +1435,11 @@ void applyActorCollisionSettings( PrimitiveComponent->SetCollisionResponseToChannels(responseContainer); } } + + // Recursively apply collision settings to children. + for (USceneComponent* pChildComponent : Component->GetAttachChildren()) { + applyActorCollisionSettings(BodyInstance, pChildComponent); + } } } // namespace @@ -1510,14 +1518,6 @@ void ACesium3DTileset::showTilesToRender( continue; } - // That looks like some reeeally entertaining debug session...: - // const Cesium3DTilesSelection::TileID& id = pTile->getTileID(); - // const CesiumGeometry::QuadtreeTileID* pQuadtreeID = - // std::get_if(&id); if (!pQuadtreeID || - // pQuadtreeID->level != 14 || pQuadtreeID->x != 5503 || pQuadtreeID->y != - // 11626) { continue; - //} - UCesiumGltfComponent* Gltf = static_cast(pTile->getRendererResources()); if (!Gltf) { @@ -1551,8 +1551,7 @@ void ACesium3DTileset::showTilesToRender( } if (!Gltf->IsVisible()) { - Gltf->SetVisibility(true, true); - Gltf->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics); + Gltf->ShowGltf(); } } } diff --git a/Source/CesiumRuntime/Private/CesiumGltfComponent.cpp b/Source/CesiumRuntime/Private/CesiumGltfComponent.cpp index 3678d9442..37e5ed1ee 100644 --- a/Source/CesiumRuntime/Private/CesiumGltfComponent.cpp +++ b/Source/CesiumRuntime/Private/CesiumGltfComponent.cpp @@ -16,8 +16,13 @@ #include "CesiumGltf/AccessorView.h" #include "CesiumGltf/ExtensionMeshPrimitiveExtFeatureMetadata.h" #include "CesiumGltf/ExtensionModelExtFeatureMetadata.h" +#include "CesiumGltf/ExtensionModelMaxarMeshVariants.h" +#include "CesiumGltf/ExtensionModelMaxarMeshVariantsValue.h" +#include "CesiumGltf/ExtensionNodeMaxarMeshVariants.h" +#include "CesiumGltf/ExtensionNodeMaxarMeshVariantsMappingsValue.h" #include "CesiumGltf/PropertyType.h" #include "CesiumGltf/TextureInfo.h" +#include "CesiumGltfMeshVariantsComponent.h" #include "CesiumGltfPrimitiveComponent.h" #include "CesiumMaterialUserData.h" #include "CesiumRasterOverlays.h" @@ -42,6 +47,7 @@ #include "StaticMeshResources.h" #include "UObject/ConstructorHelpers.h" #include "mikktspace.h" +#include #include #include #include @@ -93,7 +99,7 @@ namespace { class HalfConstructedReal : public UCesiumGltfComponent::HalfConstructed { public: virtual ~HalfConstructedReal() {} - LoadModelResult loadModelResult; + LoadModelResult loadModelResult{}; }; } // namespace @@ -1300,7 +1306,7 @@ static void loadPrimitive( } static void loadMesh( - std::optional& result, + LoadMeshResult& result, const glm::dmat4x4& transform, const CreateMeshOptions& options) { @@ -1309,16 +1315,15 @@ static void loadMesh( const Model& model = *options.pNodeOptions->pModelOptions->pModel; const Mesh& mesh = *options.pMesh; - result = LoadMeshResult(); - result->primitiveResults.reserve(mesh.primitives.size()); + result.primitiveResults.reserve(mesh.primitives.size()); for (const CesiumGltf::MeshPrimitive& primitive : mesh.primitives) { - CreatePrimitiveOptions primitiveOptions = {&options, &*result, &primitive}; - auto& primitiveResult = result->primitiveResults.emplace_back(); + CreatePrimitiveOptions primitiveOptions = {&options, &result, &primitive}; + auto& primitiveResult = result.primitiveResults.emplace_back(); loadPrimitive(primitiveResult, transform, primitiveOptions); // if it doesn't have render data, then it can't be loaded if (!primitiveResult.RenderData) { - result->primitiveResults.pop_back(); + result.primitiveResults.pop_back(); } } } @@ -1398,10 +1403,33 @@ static void loadNode( nodeTransform * translation * glm::dmat4(rotationQuat) * scale; } - int meshId = node.mesh; - if (meshId >= 0 && meshId < model.meshes.size()) { - CreateMeshOptions meshOptions = {&options, &result, &model.meshes[meshId]}; - loadMesh(result.meshResult, nodeTransform, meshOptions); + result.pVariantsExtension = + node.getExtension(); + if (result.pVariantsExtension) { + result.meshVariantsResults.reserve( + result.pVariantsExtension->mappings.size()); + for (const ExtensionNodeMaxarMeshVariantsMappingsValue& mapping : + result.pVariantsExtension->mappings) { + if (mapping.mesh >= 0 && mapping.mesh < model.meshes.size()) { + CreateMeshOptions meshOptions = { + &options, + &result, + &model.meshes[mapping.mesh]}; + auto meshResultIt = + result.meshVariantsResults.try_emplace(mapping.mesh); + loadMesh(meshResultIt.first->second, nodeTransform, meshOptions); + } + } + } else { + int meshId = node.mesh; + if (meshId >= 0 && meshId < model.meshes.size()) { + CreateMeshOptions meshOptions = { + &options, + &result, + &model.meshes[meshId]}; + result.meshResult.emplace(); + loadMesh(*result.meshResult, nodeTransform, meshOptions); + } } for (int childNodeId : node.children) { @@ -1481,6 +1509,9 @@ static void loadModelAnyThreadPart( } } + result.pVariantsExtension = + model.getExtension(); + glm::dmat4x4 rootTransform = transform; { @@ -1514,11 +1545,12 @@ static void loadModelAnyThreadPart( for (const Mesh& mesh : model.meshes) { CreateNodeOptions dummyNodeOptions = {&options, &result, nullptr}; LoadNodeResult& dummyNodeResult = result.nodeResults.emplace_back(); + dummyNodeResult.meshResult.emplace(); CreateMeshOptions meshOptions = { &dummyNodeOptions, &dummyNodeResult, &mesh}; - loadMesh(dummyNodeResult.meshResult, rootTransform, meshOptions); + loadMesh(*dummyNodeResult.meshResult, rootTransform, meshOptions); } } } @@ -1787,13 +1819,15 @@ static void SetMetadataParameterValues( } } -static void loadPrimitiveGameThreadPart( +static UCesiumGltfPrimitiveComponent* loadPrimitiveGameThreadPart( + // TODO: I don't think this pGltf is needed after a few simplifications UCesiumGltfComponent* pGltf, + USceneComponent* pOutter, LoadPrimitiveResult& loadResult, const glm::dmat4x4& cesiumToUnrealTransform) { FName meshName = createSafeName(loadResult.name, ""); UCesiumGltfPrimitiveComponent* pMesh = - NewObject(pGltf, meshName); + NewObject(pOutter, meshName); pMesh->overlayTextureCoordinateIDToUVIndex = loadResult.overlayTextureCoordinateIDToUVIndex; pMesh->textureCoordinateMap = std::move(loadResult.textureCoordinateMap); @@ -1991,8 +2025,10 @@ static void loadPrimitiveGameThreadPart( // pMesh->bDrawMeshCollisionIfComplex = true; // pMesh->bDrawMeshCollisionIfSimple = true; - pMesh->SetupAttachment(pGltf); + pMesh->SetupAttachment(pOutter); pMesh->RegisterComponent(); + + return pMesh; } /*static*/ TUniquePtr @@ -2002,7 +2038,7 @@ UCesiumGltfComponent::CreateOffGameThread( auto pResult = MakeUnique(); loadModelAnyThreadPart(pResult->loadModelResult, Transform, Options); - return pResult; + return MoveTemp(pResult); } /*static*/ UCesiumGltfComponent* UCesiumGltfComponent::CreateOnGameThread( @@ -2015,12 +2051,6 @@ UCesiumGltfComponent::CreateOffGameThread( HalfConstructedReal* pReal = static_cast(pHalfConstructed.Get()); - // TODO: was this a common case before? - // (This code checked if there were no loaded primitives in the model) - // if (result.size() == 0) { - // return nullptr; - // } - UCesiumGltfComponent* Gltf = NewObject(pParentActor); Gltf->SetUsingAbsoluteLocation(true); Gltf->SetFlags(RF_Transient | RF_DuplicateTransient | RF_TextExportTransient); @@ -2039,16 +2069,47 @@ UCesiumGltfComponent::CreateOffGameThread( Gltf->CustomDepthParameters = CustomDepthParameters; encodeMetadataGameThreadPart(Gltf->EncodedMetadata); + int32 nodeIndex = 0; for (LoadNodeResult& node : pReal->loadModelResult.nodeResults) { - if (node.meshResult) { + UCesiumGltfMeshVariantsComponent* pVariants = + UCesiumGltfMeshVariantsComponent::CreateMeshVariantsComponent( + Gltf, + // TODO: create better name, maybe during worker thread part + // instead? + createSafeName("node" + std::to_string(nodeIndex) + "_variant", ""), + pReal->loadModelResult.pVariantsExtension, + node.pVariantsExtension); + + if (pVariants) { + for (auto& meshResult : node.meshVariantsResults) { + std::vector mesh; + mesh.reserve(meshResult.second.primitiveResults.size()); + + for (LoadPrimitiveResult& primitive : + meshResult.second.primitiveResults) { + mesh.push_back(loadPrimitiveGameThreadPart( + Gltf, + pVariants, + primitive, + cesiumToUnrealTransform)); + } + + pVariants->AddMesh(meshResult.first, std::move(mesh)); + } + } else if (node.meshResult) { for (LoadPrimitiveResult& primitive : node.meshResult->primitiveResults) { - loadPrimitiveGameThreadPart(Gltf, primitive, cesiumToUnrealTransform); + loadPrimitiveGameThreadPart( + Gltf, + Gltf, + primitive, + cesiumToUnrealTransform); } } + + ++nodeIndex; } - Gltf->SetVisibility(false, true); - Gltf->SetCollisionEnabled(ECollisionEnabled::NoCollision); + Gltf->HideGltf(); return Gltf; } @@ -2080,17 +2141,37 @@ UCesiumGltfComponent::~UCesiumGltfComponent() { UE_LOG(LogCesium, VeryVerbose, TEXT("~UCesiumGltfComponent")); } -void UCesiumGltfComponent::UpdateTransformFromCesium( - const glm::dmat4& cesiumToUnrealTransform) { - for (USceneComponent* pSceneComponent : this->GetAttachChildren()) { +void UCesiumGltfComponent::ShowGltf() { + if (!this->GetVisibleFlag()) { + this->SetVisibleFlag(true); + this->OnVisibilityChanged(); + } + + for (USceneComponent* pComponent : this->GetAttachChildren()) { UCesiumGltfPrimitiveComponent* pPrimitive = - Cast(pSceneComponent); - if (pPrimitive) { - pPrimitive->UpdateTransformFromCesium(cesiumToUnrealTransform); + Cast(pComponent); + UCesiumGltfMeshVariantsComponent* pVariant = + Cast(pComponent); + + if (pPrimitive && !pPrimitive->IsVisible()) { + pPrimitive->SetVisibility(true, true); + pPrimitive->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics); + } + + if (pVariant && !pVariant->IsVisible()) { + pVariant->ShowCurrentVariant(); } } } +void UCesiumGltfComponent::HideGltf() { + this->SetVisibility(false, true); + + // TODO: check if this is recursive, do descendent components automatically + // get set to NoCollision after this call? + this->SetCollisionEnabled(ECollisionEnabled::NoCollision); +} + namespace { template diff --git a/Source/CesiumRuntime/Private/CesiumGltfComponent.h b/Source/CesiumRuntime/Private/CesiumGltfComponent.h index ace6c2668..0a16f6c6f 100644 --- a/Source/CesiumRuntime/Private/CesiumGltfComponent.h +++ b/Source/CesiumRuntime/Private/CesiumGltfComponent.h @@ -90,7 +90,9 @@ class UCesiumGltfComponent : public USceneComponent { CesiumEncodedMetadataUtility::EncodedMetadata EncodedMetadata; - void UpdateTransformFromCesium(const glm::dmat4& CesiumToUnrealTransform); + void ShowGltf(); + + void HideGltf(); void AttachRasterTile( const Cesium3DTilesSelection::Tile& Tile, diff --git a/Source/CesiumRuntime/Private/CesiumGltfMeshVariantsComponent.cpp b/Source/CesiumRuntime/Private/CesiumGltfMeshVariantsComponent.cpp new file mode 100644 index 000000000..45715bc55 --- /dev/null +++ b/Source/CesiumRuntime/Private/CesiumGltfMeshVariantsComponent.cpp @@ -0,0 +1,156 @@ + +#include "CesiumGltfMeshVariantsComponent.h" +#include "CesiumGltf/ExtensionModelMaxarMeshVariants.h" +#include "CesiumGltf/ExtensionNodeMaxarMeshVariants.h" +#include "CesiumGltfPrimitiveComponent.h" +#include "UObject/UObjectGlobals.h" +#include + +using namespace CesiumGltf; + +/*static*/ UCesiumGltfMeshVariantsComponent* +UCesiumGltfMeshVariantsComponent::CreateMeshVariantsComponent( + USceneComponent* pOutter, + const FName& name, + const ExtensionModelMaxarMeshVariants* pModelExtension, + const ExtensionNodeMaxarMeshVariants* pNodeExtension) { + + if (!pModelExtension || !pNodeExtension || + pModelExtension->defaultProperty < 0 || + pModelExtension->defaultProperty >= pModelExtension->variants.size()) { + return nullptr; + } + + UCesiumGltfMeshVariantsComponent* pVariantsComponent = + NewObject(pOutter, name); + + pVariantsComponent->_pModelMeshVariants = pModelExtension; + pVariantsComponent->_pNodeMeshVariants = pNodeExtension; + pVariantsComponent->_currentVariantIndex = + static_cast(pModelExtension->defaultProperty); + + pVariantsComponent->SetMobility(EComponentMobility::Movable); + pVariantsComponent->SetupAttachment(pOutter); + pVariantsComponent->RegisterComponent(); + + return pVariantsComponent; +} + +void UCesiumGltfMeshVariantsComponent::AddMesh( + uint32_t meshIndex, + std::vector&& mesh) { + this->_meshes.emplace(meshIndex, std::move(mesh)); +} + +FString UCesiumGltfMeshVariantsComponent::GetCurrentVariantName() const { + if (this->_pModelMeshVariants && this->_currentVariantIndex != -1) { + return FString(UTF8_TO_TCHAR( + this->_pModelMeshVariants->variants[this->_currentVariantIndex] + .name.c_str())); + } + + return ""; +} + +bool UCesiumGltfMeshVariantsComponent::SetVariantByIndex(int32 VariantIndex) { + if (VariantIndex < 0 || + VariantIndex >= this->_pModelMeshVariants->variants.size()) { + return false; + } + + this->_currentVariantIndex = VariantIndex; + ShowCurrentVariant(); + return true; +} + +bool UCesiumGltfMeshVariantsComponent::SetVariantByName(const FString& Name) { + if (this->_pModelMeshVariants) { + for (size_t i = 0; i < this->_pModelMeshVariants->variants.size(); ++i) { + const std::string& variantName = + this->_pModelMeshVariants->variants[i].name; + if (Name == FString(UTF8_TO_TCHAR(variantName.c_str()))) { + this->_currentVariantIndex = static_cast(i); + ShowCurrentVariant(); + return true; + } + } + } + + return false; +} + +static void showMesh(const std::vector& mesh) { + for (UCesiumGltfPrimitiveComponent* pPrimitive : mesh) { + if (pPrimitive && !pPrimitive->IsVisible()) { + pPrimitive->SetVisibility(true, true); + pPrimitive->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics); + } + } +} + +static void hideMesh(const std::vector& mesh) { + for (UCesiumGltfPrimitiveComponent* pPrimitive : mesh) { + if (pPrimitive && pPrimitive->IsVisible()) { + pPrimitive->SetVisibility(false, true); + pPrimitive->SetCollisionEnabled(ECollisionEnabled::NoCollision); + } + } +} + +void UCesiumGltfMeshVariantsComponent::ShowCurrentVariant() { + assert(this->_pModelMeshVariants != nullptr); + assert(this->_pNodeMeshVariants != nullptr); + assert(this->_currentVariantIndex != -1); + + if (!this->GetVisibleFlag()) { + this->SetVisibleFlag(true); + this->OnVisibilityChanged(); + } + + // Only the currently selected mesh variant should be set visible. + bool visibleMeshFound = false; + for (auto& meshIt : this->_meshes) { + if (visibleMeshFound) { + // We already found the visible mesh, so hide this mesh. + hideMesh(meshIt.second); + } else { + // Need to check if this mesh contains the current variant. + // Find the mapping for this mesh. + for (const ExtensionNodeMaxarMeshVariantsMappingsValue& mapping : + this->_pNodeMeshVariants->mappings) { + if (mapping.mesh == meshIt.first) { + // We found the mesh's mapping. + // Check if this mesh's mapping contains the current variant. + for (int32_t variantIndex : mapping.variants) { + if (variantIndex == this->_currentVariantIndex) { + visibleMeshFound = true; + break; + } + } + // Already found this mesh's mapping. + break; + } + } + + if (visibleMeshFound) { + showMesh(meshIt.second); + } else { + hideMesh(meshIt.second); + } + } + } +} + +/*static*/ +UCesiumGltfMeshVariantsComponent* +UCesiumGltfMeshVariantsBlueprintLibrary::GetMeshVariantsComponent( + UPARAM(ref) UPrimitiveComponent* Primitive) { + UCesiumGltfPrimitiveComponent* pGltfPrimitive = + Cast(Primitive); + if (!IsValid(pGltfPrimitive)) { + return nullptr; + } + + return Cast( + pGltfPrimitive->GetAttachParent()); +} diff --git a/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.cpp b/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.cpp index 0bc54f7fd..205033160 100644 --- a/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.cpp +++ b/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.cpp @@ -1,6 +1,7 @@ // Copyright 2020-2021 CesiumGS, Inc. and Contributors #include "CesiumGltfPrimitiveComponent.h" +#include "CesiumGltfMeshVariantsComponent.h" #include "CesiumLifetime.h" #include "CesiumMaterialUserData.h" #include "Engine/StaticMesh.h" diff --git a/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.h b/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.h index 730ba3d7f..59a8e1dab 100644 --- a/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.h +++ b/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.h @@ -14,6 +14,8 @@ #include #include "CesiumGltfPrimitiveComponent.generated.h" +class UCesiumGltfMeshVariantsComponent; + UCLASS() class UCesiumGltfPrimitiveComponent : public UStaticMeshComponent { GENERATED_BODY() diff --git a/Source/CesiumRuntime/Private/CreateGltfOptions.h b/Source/CesiumRuntime/Private/CreateGltfOptions.h index 686156e52..737ca9e9c 100644 --- a/Source/CesiumRuntime/Private/CreateGltfOptions.h +++ b/Source/CesiumRuntime/Private/CreateGltfOptions.h @@ -33,6 +33,7 @@ struct CreateMeshOptions { const CreateNodeOptions* pNodeOptions = nullptr; const LoadGltfResult::LoadNodeResult* pHalfConstructedNodeResult = nullptr; const CesiumGltf::Mesh* pMesh = nullptr; + std::vector variants{}; }; struct CreatePrimitiveOptions { diff --git a/Source/CesiumRuntime/Private/LoadGltfResult.h b/Source/CesiumRuntime/Private/LoadGltfResult.h index dccdbee2f..8f6f0389a 100644 --- a/Source/CesiumRuntime/Private/LoadGltfResult.h +++ b/Source/CesiumRuntime/Private/LoadGltfResult.h @@ -3,6 +3,8 @@ #pragma once #include "CesiumEncodedMetadataUtility.h" +#include "CesiumGltf/ExtensionModelMaxarMeshVariants.h" +#include "CesiumGltf/ExtensionNodeMaxarMeshVariants.h" #include "CesiumGltf/Material.h" #include "CesiumGltf/MeshPrimitive.h" #include "CesiumGltf/Model.h" @@ -19,6 +21,7 @@ #include #include #include +#include #if PHYSICS_INTERFACE_PHYSX #include "IPhysXCooking.h" @@ -48,7 +51,7 @@ struct LoadPrimitiveResult { const CesiumGltf::Material* pMaterial = nullptr; glm::dmat4x4 transform{1.0}; #if PHYSICS_INTERFACE_PHYSX - TUniquePtr pCollisionMesh; + TUniquePtr pCollisionMesh = nullptr; FBodySetupUVInfo uvInfo{}; #else TSharedPtr @@ -56,13 +59,17 @@ struct LoadPrimitiveResult { #endif std::string name{}; - TUniquePtr baseColorTexture; + TUniquePtr baseColorTexture = + nullptr; TUniquePtr - metallicRoughnessTexture; - TUniquePtr normalTexture; - TUniquePtr emissiveTexture; - TUniquePtr occlusionTexture; - TUniquePtr waterMaskTexture; + metallicRoughnessTexture = nullptr; + TUniquePtr normalTexture = nullptr; + TUniquePtr emissiveTexture = + nullptr; + TUniquePtr occlusionTexture = + nullptr; + TUniquePtr waterMaskTexture = + nullptr; std::unordered_map textureCoordinateParameters; bool onlyLand = true; @@ -76,17 +83,34 @@ struct LoadPrimitiveResult { std::unordered_map textureCoordinateMap; }; +// TODO: which ones of these explicit constructors are actually needed?? struct LoadMeshResult { - std::vector primitiveResults{}; + LoadMeshResult() = default; + LoadMeshResult(const LoadMeshResult& result) = delete; + LoadMeshResult(LoadMeshResult&& result) = default; + + std::vector primitiveResults; }; struct LoadNodeResult { + LoadNodeResult() = default; + LoadNodeResult(const LoadNodeResult& result) = delete; + LoadNodeResult(LoadNodeResult&& result) = default; + std::optional meshResult = std::nullopt; + std::unordered_map meshVariantsResults; + const CesiumGltf::ExtensionNodeMaxarMeshVariants* pVariantsExtension = + nullptr; }; struct LoadModelResult { - std::vector nodeResults{}; + LoadModelResult() = default; + LoadModelResult(const LoadModelResult& result) = delete; + + std::vector nodeResults; FCesiumMetadataModel Metadata{}; CesiumEncodedMetadataUtility::EncodedMetadata EncodedMetadata{}; + const CesiumGltf::ExtensionModelMaxarMeshVariants* pVariantsExtension = + nullptr; }; } // namespace LoadGltfResult diff --git a/Source/CesiumRuntime/Public/CesiumGltfMeshVariantsComponent.h b/Source/CesiumRuntime/Public/CesiumGltfMeshVariantsComponent.h new file mode 100644 index 000000000..1683deb59 --- /dev/null +++ b/Source/CesiumRuntime/Public/CesiumGltfMeshVariantsComponent.h @@ -0,0 +1,77 @@ +#pragma once + +#include "Components/SceneComponent.h" +#include "Containers/Array.h" +#include "Containers/Map.h" +#include "GenericPlatform/GenericPlatform.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include +#include +#include +#include "CesiumGltfMeshVariantsComponent.generated.h" + +namespace CesiumGltf { +struct Model; +struct Node; +struct ExtensionModelMaxarMeshVariants; +struct ExtensionNodeMaxarMeshVariants; +struct ExtensionModelMaxarMeshVariantsValue; +} // namespace CesiumGltf + +class UCesiumGltfPrimitiveComponent; + +UCLASS(BlueprintType) +class CESIUMRUNTIME_API UCesiumGltfMeshVariantsComponent + : public USceneComponent { + GENERATED_BODY() + +public: + UCesiumGltfMeshVariantsComponent() = default; + virtual ~UCesiumGltfMeshVariantsComponent() = default; + + void AddMesh( + uint32_t meshIndex, + std::vector&& mesh); + + UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Cesium|MeshVariants") + int32 GetCurrentVariantIndex() const { return this->_currentVariantIndex; } + + UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Cesium|MeshVariants") + FString GetCurrentVariantName() const; + + UFUNCTION(BlueprintCallable, Category = "Cesium|MeshVariants") + bool SetVariantByIndex(int32 VariantIndex); + + UFUNCTION(BlueprintCallable, Category = "Cesium|MeshVariants") + bool SetVariantByName(const FString& Name); + + void ShowCurrentVariant(); + + static UCesiumGltfMeshVariantsComponent* CreateMeshVariantsComponent( + USceneComponent* pOutter, + const FName& name, + const CesiumGltf::ExtensionModelMaxarMeshVariants* pModelExtension, + const CesiumGltf::ExtensionNodeMaxarMeshVariants* pNodeExtension); + +private: + const CesiumGltf::ExtensionModelMaxarMeshVariants* _pModelMeshVariants; + const CesiumGltf::ExtensionNodeMaxarMeshVariants* _pNodeMeshVariants; + + int32_t _currentVariantIndex = -1; + + std::unordered_map> + _meshes; + + friend UCesiumGltfMeshVariantsBlueprintLibrary; +}; + +UCLASS() +class CESIUMRUNTIME_API UCesiumGltfMeshVariantsBlueprintLibrary + : public UBlueprintFunctionLibrary { + GENERATED_BODY() + +public: + UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Cesium|MeshVariants") + static UCesiumGltfMeshVariantsComponent* + GetMeshVariantsComponent(UPARAM(ref) UPrimitiveComponent* Primitive); +};