1255 lines
43 KiB
C++
1255 lines
43 KiB
C++
// Copyright 2020-2024 CesiumGS, Inc. and Contributors
|
|
|
|
#include "CesiumEncodedFeaturesMetadata.h"
|
|
#include "CesiumEncodedMetadataConversions.h"
|
|
#include "CesiumFeatureIdSet.h"
|
|
#include "CesiumFeaturesMetadataComponent.h"
|
|
#include "CesiumLifetime.h"
|
|
#include "CesiumModelMetadata.h"
|
|
#include "CesiumPrimitiveFeatures.h"
|
|
#include "CesiumPrimitiveMetadata.h"
|
|
#include "CesiumPropertyArray.h"
|
|
#include "CesiumPropertyTable.h"
|
|
#include "CesiumPropertyTexture.h"
|
|
#include "CesiumRuntime.h"
|
|
#include "Containers/Map.h"
|
|
#include "Materials/MaterialInstanceDynamic.h"
|
|
#include "PixelFormat.h"
|
|
#include "TextureResource.h"
|
|
#include "UnrealMetadataConversions.h"
|
|
|
|
#include <CesiumGltf/FeatureIdTextureView.h>
|
|
#include <CesiumUtility/Tracing.h>
|
|
#include <optional>
|
|
#include <unordered_map>
|
|
|
|
using namespace CesiumTextureUtility;
|
|
|
|
namespace CesiumEncodedFeaturesMetadata {
|
|
|
|
FString getNameForFeatureIDSet(
|
|
const FCesiumFeatureIdSet& featureIDSet,
|
|
int32& FeatureIdTextureCounter) {
|
|
FString label = UCesiumFeatureIdSetBlueprintLibrary::GetLabel(featureIDSet);
|
|
if (!label.IsEmpty()) {
|
|
return label;
|
|
}
|
|
|
|
ECesiumFeatureIdSetType type =
|
|
UCesiumFeatureIdSetBlueprintLibrary::GetFeatureIDSetType(featureIDSet);
|
|
|
|
if (type == ECesiumFeatureIdSetType::Attribute) {
|
|
FCesiumFeatureIdAttribute attribute =
|
|
UCesiumFeatureIdSetBlueprintLibrary::GetAsFeatureIDAttribute(
|
|
featureIDSet);
|
|
ECesiumFeatureIdAttributeStatus status =
|
|
UCesiumFeatureIdAttributeBlueprintLibrary::GetFeatureIDAttributeStatus(
|
|
attribute);
|
|
if (status == ECesiumFeatureIdAttributeStatus::Valid) {
|
|
std::string generatedName =
|
|
"_FEATURE_ID_" + std::to_string(attribute.getAttributeIndex());
|
|
return FString(generatedName.c_str());
|
|
}
|
|
}
|
|
|
|
if (type == ECesiumFeatureIdSetType::Instance) {
|
|
FCesiumFeatureIdAttribute attribute =
|
|
UCesiumFeatureIdSetBlueprintLibrary::GetAsFeatureIDAttribute(
|
|
featureIDSet);
|
|
ECesiumFeatureIdAttributeStatus status =
|
|
UCesiumFeatureIdAttributeBlueprintLibrary::GetFeatureIDAttributeStatus(
|
|
attribute);
|
|
if (status == ECesiumFeatureIdAttributeStatus::Valid) {
|
|
std::string generatedName = "_FEATURE_INSTANCE_ID_" +
|
|
std::to_string(attribute.getAttributeIndex());
|
|
return FString(generatedName.c_str());
|
|
}
|
|
}
|
|
|
|
if (type == ECesiumFeatureIdSetType::Texture) {
|
|
std::string generatedName =
|
|
"_FEATURE_ID_TEXTURE_" + std::to_string(FeatureIdTextureCounter);
|
|
FeatureIdTextureCounter++;
|
|
return FString(generatedName.c_str());
|
|
}
|
|
|
|
if (type == ECesiumFeatureIdSetType::Implicit) {
|
|
return FString("_IMPLICIT_FEATURE_ID");
|
|
}
|
|
|
|
if (type == ECesiumFeatureIdSetType::InstanceImplicit) {
|
|
return FString("_IMPLICIT_FEATURE_INSTANCE_ID");
|
|
}
|
|
|
|
// If for some reason an empty / invalid feature ID set was constructed,
|
|
// return an empty name.
|
|
return FString();
|
|
}
|
|
|
|
namespace {
|
|
|
|
/**
|
|
* @brief Encodes a feature ID attribute for access in a Unreal Engine Material.
|
|
* The feature IDs are simply sent to the GPU as texture coordinates, so this
|
|
* just handles the variable names necessary for material access.
|
|
*
|
|
* @returns The encoded feature ID attribute, or std::nullopt if the attribute
|
|
* was somehow invalid.
|
|
*/
|
|
std::optional<EncodedFeatureIdSet>
|
|
encodeFeatureIdAttribute(const FCesiumFeatureIdAttribute& attribute) {
|
|
const ECesiumFeatureIdAttributeStatus status =
|
|
UCesiumFeatureIdAttributeBlueprintLibrary::GetFeatureIDAttributeStatus(
|
|
attribute);
|
|
|
|
if (status != ECesiumFeatureIdAttributeStatus::Valid) {
|
|
UE_LOG(
|
|
LogCesium,
|
|
Warning,
|
|
TEXT("Can't encode invalid feature ID attribute, skipped."));
|
|
return std::nullopt;
|
|
}
|
|
|
|
EncodedFeatureIdSet result;
|
|
result.attribute = attribute.getAttributeIndex();
|
|
return result;
|
|
}
|
|
|
|
std::optional<EncodedFeatureIdSet> encodeFeatureIdTexture(
|
|
const FCesiumFeatureIdTexture& texture,
|
|
TMap<const CesiumGltf::ImageAsset*, TWeakPtr<LoadedTextureResult>>&
|
|
featureIdTextureMap) {
|
|
const ECesiumFeatureIdTextureStatus status =
|
|
UCesiumFeatureIdTextureBlueprintLibrary::GetFeatureIDTextureStatus(
|
|
texture);
|
|
if (status != ECesiumFeatureIdTextureStatus::Valid) {
|
|
UE_LOG(
|
|
LogCesium,
|
|
Warning,
|
|
TEXT("Can't encode invalid feature ID texture, skipped."));
|
|
return std::nullopt;
|
|
}
|
|
|
|
const CesiumGltf::FeatureIdTextureView& featureIdTextureView =
|
|
texture.getFeatureIdTextureView();
|
|
const CesiumGltf::ImageAsset* pFeatureIdImage =
|
|
featureIdTextureView.getImage();
|
|
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::EncodeFeatureIdTexture)
|
|
|
|
EncodedFeatureIdSet result;
|
|
EncodedFeatureIdTexture& encodedFeatureIdTexture = result.texture.emplace();
|
|
|
|
encodedFeatureIdTexture.channels = featureIdTextureView.getChannels();
|
|
encodedFeatureIdTexture.textureCoordinateSetIndex =
|
|
featureIdTextureView.getTexCoordSetIndex();
|
|
encodedFeatureIdTexture.textureTransform =
|
|
featureIdTextureView.getTextureTransform();
|
|
|
|
TWeakPtr<LoadedTextureResult>* pMappedUnrealImageIt =
|
|
featureIdTextureMap.Find(pFeatureIdImage);
|
|
if (pMappedUnrealImageIt) {
|
|
encodedFeatureIdTexture.pTexture = pMappedUnrealImageIt->Pin();
|
|
} else {
|
|
TextureAddress addressX = TextureAddress::TA_Wrap;
|
|
TextureAddress addressY = TextureAddress::TA_Wrap;
|
|
|
|
const CesiumGltf::Sampler* pSampler = featureIdTextureView.getSampler();
|
|
if (pSampler) {
|
|
addressX = convertGltfWrapSToUnreal(pSampler->wrapS);
|
|
addressY = convertGltfWrapTToUnreal(pSampler->wrapT);
|
|
}
|
|
|
|
// Copy the image, so that we can keep a copy of it in the glTF.
|
|
CesiumUtility::IntrusivePointer<CesiumGltf::ImageAsset> pImageCopy =
|
|
new CesiumGltf::ImageAsset(*pFeatureIdImage);
|
|
encodedFeatureIdTexture.pTexture =
|
|
MakeShared<LoadedTextureResult>(std::move(*loadTextureAnyThreadPart(
|
|
*pImageCopy,
|
|
addressX,
|
|
addressY,
|
|
TextureFilter::TF_Nearest,
|
|
false,
|
|
TEXTUREGROUP_8BitData,
|
|
false,
|
|
// TODO: currently this is always the case, but doesn't have to be
|
|
EPixelFormat::PF_R8G8B8A8_UINT)));
|
|
featureIdTextureMap.Emplace(
|
|
pFeatureIdImage,
|
|
encodedFeatureIdTexture.pTexture);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
EncodedPrimitiveFeatures encodePrimitiveFeaturesAnyThreadPart(
|
|
const FCesiumPrimitiveFeaturesDescription& featuresDescription,
|
|
const FCesiumPrimitiveFeatures& features) {
|
|
EncodedPrimitiveFeatures result;
|
|
|
|
const TArray<FCesiumFeatureIdSetDescription>& featureIDSetDescriptions =
|
|
featuresDescription.FeatureIdSets;
|
|
result.featureIdSets.Reserve(featureIDSetDescriptions.Num());
|
|
|
|
// Not all feature ID sets are necessarily textures, but reserve the max
|
|
// amount just in case.
|
|
TMap<const CesiumGltf::ImageAsset*, TWeakPtr<LoadedTextureResult>>
|
|
featureIdTextureMap;
|
|
featureIdTextureMap.Reserve(featureIDSetDescriptions.Num());
|
|
|
|
const TArray<FCesiumFeatureIdSet>& featureIdSets =
|
|
UCesiumPrimitiveFeaturesBlueprintLibrary::GetFeatureIDSets(features);
|
|
int32_t featureIdTextureCounter = 0;
|
|
|
|
for (int32 i = 0; i < featureIdSets.Num(); i++) {
|
|
const FCesiumFeatureIdSet& set = featureIdSets[i];
|
|
FString name = getNameForFeatureIDSet(set, featureIdTextureCounter);
|
|
const FCesiumFeatureIdSetDescription* pDescription =
|
|
featureIDSetDescriptions.FindByPredicate(
|
|
[&name](
|
|
const FCesiumFeatureIdSetDescription& existingFeatureIDSet) {
|
|
return existingFeatureIDSet.Name == name;
|
|
});
|
|
|
|
if (!pDescription) {
|
|
// The description doesn't need this feature ID set, skip.
|
|
continue;
|
|
}
|
|
|
|
std::optional<EncodedFeatureIdSet> encodedSet;
|
|
ECesiumFeatureIdSetType type =
|
|
UCesiumFeatureIdSetBlueprintLibrary::GetFeatureIDSetType(set);
|
|
|
|
if (type == ECesiumFeatureIdSetType::Attribute) {
|
|
const FCesiumFeatureIdAttribute& attribute =
|
|
UCesiumFeatureIdSetBlueprintLibrary::GetAsFeatureIDAttribute(set);
|
|
encodedSet = encodeFeatureIdAttribute(attribute);
|
|
} else if (type == ECesiumFeatureIdSetType::Texture) {
|
|
const FCesiumFeatureIdTexture& texture =
|
|
UCesiumFeatureIdSetBlueprintLibrary::GetAsFeatureIDTexture(set);
|
|
encodedSet = encodeFeatureIdTexture(texture, featureIdTextureMap);
|
|
} else if (type == ECesiumFeatureIdSetType::Implicit) {
|
|
encodedSet = EncodedFeatureIdSet();
|
|
}
|
|
|
|
if (!encodedSet)
|
|
continue;
|
|
|
|
encodedSet->name = name;
|
|
encodedSet->index = i;
|
|
encodedSet->propertyTableName = pDescription->PropertyTableName;
|
|
encodedSet->nullFeatureId =
|
|
UCesiumFeatureIdSetBlueprintLibrary::GetNullFeatureID(set);
|
|
|
|
result.featureIdSets.Add(*encodedSet);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool encodePrimitiveFeaturesGameThreadPart(
|
|
EncodedPrimitiveFeatures& encodedFeatures) {
|
|
bool success = true;
|
|
|
|
// Not all feature ID sets are necessarily textures, but reserve the max
|
|
// amount just in case.
|
|
TArray<const LoadedTextureResult*> uniqueFeatureIdImages;
|
|
uniqueFeatureIdImages.Reserve(encodedFeatures.featureIdSets.Num());
|
|
|
|
for (EncodedFeatureIdSet& encodedFeatureIdSet :
|
|
encodedFeatures.featureIdSets) {
|
|
if (!encodedFeatureIdSet.texture) {
|
|
continue;
|
|
}
|
|
|
|
auto& encodedFeatureIdTexture = *encodedFeatureIdSet.texture;
|
|
if (uniqueFeatureIdImages.Find(encodedFeatureIdTexture.pTexture.Get()) ==
|
|
INDEX_NONE) {
|
|
success &= loadTextureGameThreadPart(
|
|
encodedFeatureIdTexture.pTexture.Get()) != nullptr;
|
|
uniqueFeatureIdImages.Emplace(encodedFeatureIdTexture.pTexture.Get());
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
void destroyEncodedPrimitiveFeatures(
|
|
EncodedPrimitiveFeatures& encodedFeatures) {
|
|
for (EncodedFeatureIdSet& encodedFeatureIdSet :
|
|
encodedFeatures.featureIdSets) {
|
|
if (!encodedFeatureIdSet.texture) {
|
|
continue;
|
|
}
|
|
|
|
auto& encodedFeatureIdTexture = *encodedFeatureIdSet.texture;
|
|
if (encodedFeatureIdTexture.pTexture) {
|
|
encodedFeatureIdTexture.pTexture->pTexture = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
FString getNameForPropertyTable(const FCesiumPropertyTable& PropertyTable) {
|
|
FString propertyTableName =
|
|
UCesiumPropertyTableBlueprintLibrary::GetPropertyTableName(PropertyTable);
|
|
|
|
if (propertyTableName.IsEmpty()) {
|
|
// Substitute the name with the property table's class.
|
|
propertyTableName = PropertyTable.getClassName();
|
|
}
|
|
|
|
return propertyTableName;
|
|
}
|
|
|
|
FString
|
|
getNameForPropertyTexture(const FCesiumPropertyTexture& PropertyTexture) {
|
|
FString propertyTextureName =
|
|
UCesiumPropertyTextureBlueprintLibrary::GetPropertyTextureName(
|
|
PropertyTexture);
|
|
|
|
if (propertyTextureName.IsEmpty()) {
|
|
// Substitute the name with the property texture's class.
|
|
propertyTextureName = PropertyTexture.getClassName();
|
|
}
|
|
|
|
return propertyTextureName;
|
|
}
|
|
|
|
FString getMaterialNameForPropertyTableProperty(
|
|
const FString& propertyTableName,
|
|
const FString& propertyName) {
|
|
// Example: "PTABLE_houses_roofColor"
|
|
return createHlslSafeName(
|
|
MaterialPropertyTablePrefix + propertyTableName + "_" + propertyName);
|
|
}
|
|
|
|
FString getMaterialNameForPropertyTextureProperty(
|
|
const FString& propertyTextureName,
|
|
const FString& propertyName) {
|
|
// Example: "PTEXTURE_house_temperature"
|
|
return createHlslSafeName(
|
|
MaterialPropertyTexturePrefix + propertyTextureName + "_" + propertyName);
|
|
}
|
|
|
|
namespace {
|
|
|
|
bool isValidPropertyTablePropertyDescription(
|
|
const FCesiumPropertyTablePropertyDescription& propertyDescription,
|
|
const FCesiumPropertyTableProperty& property) {
|
|
if (propertyDescription.EncodingDetails.Type ==
|
|
ECesiumEncodedMetadataType::None) {
|
|
UE_LOG(
|
|
LogCesium,
|
|
Warning,
|
|
TEXT(
|
|
"No encoded metadata type was specified for this property table property; skip encoding."));
|
|
return false;
|
|
}
|
|
|
|
if (propertyDescription.EncodingDetails.ComponentType ==
|
|
ECesiumEncodedMetadataComponentType::None) {
|
|
UE_LOG(
|
|
LogCesium,
|
|
Warning,
|
|
TEXT(
|
|
"No encoded metadata component type was specified for this property table property; skip encoding."));
|
|
return false;
|
|
}
|
|
|
|
const FCesiumMetadataValueType expectedType =
|
|
propertyDescription.PropertyDetails.GetValueType();
|
|
const FCesiumMetadataValueType valueType =
|
|
UCesiumPropertyTablePropertyBlueprintLibrary::GetValueType(property);
|
|
if (valueType != expectedType) {
|
|
UE_LOG(
|
|
LogCesium,
|
|
Warning,
|
|
TEXT(
|
|
"The value type of the metadata property %s does not match the type specified by the metadata description. It will still attempt to be encoded, but may result in empty or unexpected values."),
|
|
*propertyDescription.Name);
|
|
}
|
|
|
|
bool isNormalized =
|
|
UCesiumPropertyTablePropertyBlueprintLibrary::IsNormalized(property);
|
|
if (propertyDescription.PropertyDetails.bIsNormalized != isNormalized) {
|
|
FString error =
|
|
propertyDescription.PropertyDetails.bIsNormalized
|
|
? "Description incorrectly marked a property table property as normalized; skip encoding."
|
|
: "Description incorrectly marked a property table property as not normalized; skip encoding.";
|
|
UE_LOG(LogCesium, Warning, TEXT("%s"), *error);
|
|
return false;
|
|
}
|
|
|
|
// Only uint8 normalization is currently supported.
|
|
if (isNormalized &&
|
|
valueType.ComponentType != ECesiumMetadataComponentType::Uint8) {
|
|
UE_LOG(
|
|
LogCesium,
|
|
Warning,
|
|
TEXT("Only normalization of uint8 properties is currently supported."));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool isValidPropertyTexturePropertyDescription(
|
|
const FCesiumPropertyTexturePropertyDescription& propertyDescription,
|
|
const FCesiumPropertyTextureProperty& property) {
|
|
const FCesiumMetadataValueType expectedType =
|
|
propertyDescription.PropertyDetails.GetValueType();
|
|
const FCesiumMetadataValueType valueType =
|
|
UCesiumPropertyTexturePropertyBlueprintLibrary::GetValueType(property);
|
|
if (valueType != expectedType) {
|
|
UE_LOG(
|
|
LogCesium,
|
|
Warning,
|
|
TEXT(
|
|
"The value type of the metadata property %s does not match the type specified by the metadata description. It will still attempt to be encoded, but may result in empty or unexpected values."),
|
|
*propertyDescription.Name);
|
|
}
|
|
|
|
bool isNormalized =
|
|
UCesiumPropertyTexturePropertyBlueprintLibrary::IsNormalized(property);
|
|
if (propertyDescription.PropertyDetails.bIsNormalized != isNormalized) {
|
|
FString error =
|
|
propertyDescription.PropertyDetails.bIsNormalized
|
|
? "Description incorrectly marked a property texture property as normalized; skip encoding."
|
|
: "Description incorrectly marked a property texture property as not normalized; skip encoding.";
|
|
UE_LOG(LogCesium, Warning, TEXT("%s"), *error);
|
|
return false;
|
|
}
|
|
|
|
// Only uint8 normalization is currently supported.
|
|
if (isNormalized &&
|
|
valueType.ComponentType != ECesiumMetadataComponentType::Uint8) {
|
|
UE_LOG(
|
|
LogCesium,
|
|
Warning,
|
|
TEXT("Only normalization of uint8 properties is currently supported."));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
EncodedPropertyTable encodePropertyTableAnyThreadPart(
|
|
const FCesiumPropertyTableDescription& propertyTableDescription,
|
|
const FCesiumPropertyTable& propertyTable) {
|
|
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::EncodePropertyTable)
|
|
|
|
EncodedPropertyTable encodedPropertyTable;
|
|
|
|
int64 propertyTableCount =
|
|
UCesiumPropertyTableBlueprintLibrary::GetPropertyTableCount(
|
|
propertyTable);
|
|
|
|
const TMap<FString, FCesiumPropertyTableProperty>& properties =
|
|
UCesiumPropertyTableBlueprintLibrary::GetProperties(propertyTable);
|
|
|
|
encodedPropertyTable.properties.Reserve(properties.Num());
|
|
for (const auto& pair : properties) {
|
|
const FCesiumPropertyTableProperty& property = pair.Value;
|
|
|
|
const FCesiumPropertyTablePropertyDescription* pDescription =
|
|
propertyTableDescription.Properties.FindByPredicate(
|
|
[&key = pair.Key](const FCesiumPropertyTablePropertyDescription&
|
|
expectedProperty) {
|
|
return key == expectedProperty.Name;
|
|
});
|
|
|
|
if (!pDescription) {
|
|
continue;
|
|
}
|
|
|
|
const FCesiumMetadataEncodingDetails& encodingDetails =
|
|
pDescription->EncodingDetails;
|
|
if (encodingDetails.Conversion == ECesiumEncodedMetadataConversion::None) {
|
|
// No encoding to be done; skip.
|
|
continue;
|
|
}
|
|
|
|
if (!isValidPropertyTablePropertyDescription(*pDescription, property)) {
|
|
continue;
|
|
}
|
|
|
|
if (encodingDetails.Conversion ==
|
|
ECesiumEncodedMetadataConversion::Coerce &&
|
|
!CesiumEncodedMetadataCoerce::canEncode(*pDescription)) {
|
|
UE_LOG(
|
|
LogCesium,
|
|
Warning,
|
|
TEXT(
|
|
"Cannot use 'Coerce' with the specified property info; skipped."));
|
|
continue;
|
|
}
|
|
|
|
if (encodingDetails.Conversion ==
|
|
ECesiumEncodedMetadataConversion::ParseColorFromString &&
|
|
!CesiumEncodedMetadataParseColorFromString::canEncode(*pDescription)) {
|
|
UE_LOG(
|
|
LogCesium,
|
|
Warning,
|
|
TEXT(
|
|
"Cannot use `Parse Color From String` with the specified property info; skipped."));
|
|
continue;
|
|
}
|
|
|
|
EncodedPixelFormat encodedFormat =
|
|
getPixelFormat(encodingDetails.Type, encodingDetails.ComponentType);
|
|
if (encodedFormat.format == EPixelFormat::PF_Unknown) {
|
|
UE_LOG(
|
|
LogCesium,
|
|
Warning,
|
|
TEXT(
|
|
"Unable to determine a suitable GPU format for this property table property; skipped."));
|
|
continue;
|
|
}
|
|
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::EncodePropertyTableProperty)
|
|
|
|
EncodedPropertyTableProperty& encodedProperty =
|
|
encodedPropertyTable.properties.Emplace_GetRef();
|
|
encodedProperty.name = createHlslSafeName(pDescription->Name);
|
|
encodedProperty.type = pDescription->EncodingDetails.Type;
|
|
|
|
if (UCesiumPropertyTablePropertyBlueprintLibrary::
|
|
GetPropertyTablePropertyStatus(property) ==
|
|
ECesiumPropertyTablePropertyStatus::Valid) {
|
|
|
|
int64 floorSqrtFeatureCount = glm::sqrt(propertyTableCount);
|
|
int64 textureDimension =
|
|
(floorSqrtFeatureCount * floorSqrtFeatureCount == propertyTableCount)
|
|
? floorSqrtFeatureCount
|
|
: (floorSqrtFeatureCount + 1);
|
|
|
|
CesiumUtility::IntrusivePointer<CesiumGltf::ImageAsset> pImage =
|
|
new CesiumGltf::ImageAsset();
|
|
pImage->width = pImage->height = textureDimension;
|
|
pImage->bytesPerChannel = encodedFormat.bytesPerChannel;
|
|
pImage->channels = encodedFormat.channels;
|
|
pImage->pixelData.resize(
|
|
textureDimension * textureDimension * encodedFormat.bytesPerChannel *
|
|
encodedFormat.channels);
|
|
|
|
if (encodingDetails.Conversion ==
|
|
ECesiumEncodedMetadataConversion::ParseColorFromString) {
|
|
CesiumEncodedMetadataParseColorFromString::encode(
|
|
*pDescription,
|
|
property,
|
|
std::span(pImage->pixelData),
|
|
encodedFormat.bytesPerChannel * encodedFormat.channels);
|
|
} else /* info.Conversion == ECesiumEncodedMetadataConversion::Coerce */ {
|
|
CesiumEncodedMetadataCoerce::encode(
|
|
*pDescription,
|
|
property,
|
|
std::span(pImage->pixelData),
|
|
encodedFormat.bytesPerChannel * encodedFormat.channels);
|
|
}
|
|
|
|
encodedProperty.pTexture = loadTextureAnyThreadPart(
|
|
*pImage,
|
|
TextureAddress::TA_Clamp,
|
|
TextureAddress::TA_Clamp,
|
|
TextureFilter::TF_Nearest,
|
|
false,
|
|
TEXTUREGROUP_8BitData,
|
|
false,
|
|
encodedFormat.format);
|
|
}
|
|
|
|
if (pDescription->PropertyDetails.bHasOffset) {
|
|
// If no offset is provided, default to 0, as specified by the spec.
|
|
FCesiumMetadataValue value =
|
|
UCesiumPropertyTablePropertyBlueprintLibrary::GetOffset(property);
|
|
encodedProperty.offset =
|
|
!UCesiumMetadataValueBlueprintLibrary::IsEmpty(value)
|
|
? value
|
|
: FCesiumMetadataValue(0);
|
|
}
|
|
|
|
if (pDescription->PropertyDetails.bHasScale) {
|
|
// If no scale is provided, default to 1, as specified by the spec.
|
|
FCesiumMetadataValue value =
|
|
UCesiumPropertyTablePropertyBlueprintLibrary::GetScale(property);
|
|
encodedProperty.scale =
|
|
!UCesiumMetadataValueBlueprintLibrary::IsEmpty(value)
|
|
? value
|
|
: FCesiumMetadataValue(1);
|
|
}
|
|
|
|
if (pDescription->PropertyDetails.bHasNoDataValue) {
|
|
FCesiumMetadataValue value =
|
|
UCesiumPropertyTablePropertyBlueprintLibrary::GetNoDataValue(
|
|
property);
|
|
encodedProperty.noData =
|
|
!UCesiumMetadataValueBlueprintLibrary::IsEmpty(value)
|
|
? value
|
|
: FCesiumMetadataValue(0);
|
|
}
|
|
|
|
if (pDescription->PropertyDetails.bHasDefaultValue) {
|
|
FCesiumMetadataValue value =
|
|
UCesiumPropertyTablePropertyBlueprintLibrary::GetDefaultValue(
|
|
property);
|
|
encodedProperty.defaultValue =
|
|
!UCesiumMetadataValueBlueprintLibrary::IsEmpty(value)
|
|
? value
|
|
: FCesiumMetadataValue(0);
|
|
}
|
|
}
|
|
|
|
return encodedPropertyTable;
|
|
}
|
|
|
|
EncodedPropertyTexture encodePropertyTextureAnyThreadPart(
|
|
const FCesiumPropertyTextureDescription& propertyTextureDescription,
|
|
const FCesiumPropertyTexture& propertyTexture,
|
|
TMap<const CesiumGltf::ImageAsset*, TWeakPtr<LoadedTextureResult>>&
|
|
propertyTexturePropertyMap) {
|
|
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::EncodePropertyTexture)
|
|
|
|
EncodedPropertyTexture encodedPropertyTexture;
|
|
|
|
const TMap<FString, FCesiumPropertyTextureProperty>& properties =
|
|
UCesiumPropertyTextureBlueprintLibrary::GetProperties(propertyTexture);
|
|
|
|
encodedPropertyTexture.properties.Reserve(properties.Num());
|
|
|
|
for (const auto& pair : properties) {
|
|
const FCesiumPropertyTextureProperty& property = pair.Value;
|
|
|
|
const FCesiumPropertyTexturePropertyDescription* pDescription =
|
|
propertyTextureDescription.Properties.FindByPredicate(
|
|
[&key = pair.Key](const FCesiumPropertyTexturePropertyDescription&
|
|
expectedProperty) {
|
|
return key == expectedProperty.Name;
|
|
});
|
|
|
|
if (!pDescription) {
|
|
continue;
|
|
}
|
|
|
|
if (!isValidPropertyTexturePropertyDescription(*pDescription, property)) {
|
|
continue;
|
|
}
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::EncodePropertyTextureProperty)
|
|
|
|
EncodedPropertyTextureProperty& encodedProperty =
|
|
encodedPropertyTexture.properties.Emplace_GetRef();
|
|
encodedProperty.name = createHlslSafeName(pDescription->Name);
|
|
encodedProperty.type =
|
|
CesiumMetadataTypeToEncodingType(pDescription->PropertyDetails.Type);
|
|
encodedProperty.textureCoordinateSetIndex = property.getTexCoordSetIndex();
|
|
|
|
if (UCesiumPropertyTexturePropertyBlueprintLibrary::
|
|
GetPropertyTexturePropertyStatus(property) ==
|
|
ECesiumPropertyTexturePropertyStatus::Valid) {
|
|
|
|
const TArray<int64>& channels =
|
|
UCesiumPropertyTexturePropertyBlueprintLibrary::GetChannels(property);
|
|
const int32 channelCount = channels.Num();
|
|
for (int32 i = 0; i < channelCount; i++) {
|
|
encodedProperty.channels[i] = channels[i];
|
|
}
|
|
|
|
const CesiumGltf::ImageAsset* pImage = property.getImage();
|
|
|
|
TWeakPtr<LoadedTextureResult>* pMappedUnrealImageIt =
|
|
propertyTexturePropertyMap.Find(pImage);
|
|
if (pMappedUnrealImageIt) {
|
|
encodedProperty.pTexture = pMappedUnrealImageIt->Pin();
|
|
} else {
|
|
TextureAddress addressX = TextureAddress::TA_Wrap;
|
|
TextureAddress addressY = TextureAddress::TA_Wrap;
|
|
|
|
const CesiumGltf::Sampler* pSampler = property.getSampler();
|
|
if (pSampler) {
|
|
addressX = convertGltfWrapSToUnreal(pSampler->wrapS);
|
|
addressY = convertGltfWrapTToUnreal(pSampler->wrapT);
|
|
}
|
|
|
|
// Copy the image, so that we can keep a copy of it in the glTF.
|
|
CesiumUtility::IntrusivePointer<CesiumGltf::ImageAsset> pImageCopy =
|
|
new CesiumGltf::ImageAsset(*pImage);
|
|
encodedProperty.pTexture =
|
|
MakeShared<LoadedTextureResult>(std::move(*loadTextureAnyThreadPart(
|
|
*pImageCopy,
|
|
addressX,
|
|
addressY,
|
|
// TODO: account for texture filter
|
|
TextureFilter::TF_Nearest,
|
|
false,
|
|
TEXTUREGROUP_8BitData,
|
|
false,
|
|
// This assumes that the texture's image only contains one byte
|
|
// per channel.
|
|
EPixelFormat::PF_R8G8B8A8_UINT)));
|
|
propertyTexturePropertyMap.Emplace(pImage, encodedProperty.pTexture);
|
|
}
|
|
};
|
|
|
|
if (pDescription->PropertyDetails.bHasOffset) {
|
|
encodedProperty.offset =
|
|
UCesiumPropertyTexturePropertyBlueprintLibrary::GetOffset(property);
|
|
}
|
|
|
|
if (pDescription->PropertyDetails.bHasScale) {
|
|
encodedProperty.scale =
|
|
UCesiumPropertyTexturePropertyBlueprintLibrary::GetScale(property);
|
|
}
|
|
|
|
if (pDescription->PropertyDetails.bHasNoDataValue) {
|
|
encodedProperty.noData =
|
|
UCesiumPropertyTexturePropertyBlueprintLibrary::GetNoDataValue(
|
|
property);
|
|
}
|
|
|
|
if (pDescription->PropertyDetails.bHasDefaultValue) {
|
|
encodedProperty.defaultValue =
|
|
UCesiumPropertyTexturePropertyBlueprintLibrary::GetDefaultValue(
|
|
property);
|
|
}
|
|
|
|
if (pDescription->bHasKhrTextureTransform) {
|
|
encodedProperty.textureTransform = property.getTextureTransform();
|
|
}
|
|
}
|
|
|
|
return encodedPropertyTexture;
|
|
}
|
|
|
|
EncodedPrimitiveMetadata encodePrimitiveMetadataAnyThreadPart(
|
|
const FCesiumPrimitiveMetadataDescription& metadataDescription,
|
|
const FCesiumPrimitiveMetadata& primitiveMetadata,
|
|
const FCesiumModelMetadata& modelMetadata) {
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::EncodeMetadataPrimitive)
|
|
|
|
EncodedPrimitiveMetadata result;
|
|
|
|
const TArray<FCesiumPropertyTexture>& propertyTextures =
|
|
UCesiumModelMetadataBlueprintLibrary::GetPropertyTextures(modelMetadata);
|
|
result.propertyTextureIndices.Reserve(
|
|
metadataDescription.PropertyTextureNames.Num());
|
|
|
|
for (int32 i = 0; i < propertyTextures.Num(); i++) {
|
|
const FCesiumPropertyTexture& propertyTexture = propertyTextures[i];
|
|
FString propertyTextureName = getNameForPropertyTexture(propertyTexture);
|
|
const FString* pName =
|
|
metadataDescription.PropertyTextureNames.Find(propertyTextureName);
|
|
// Confirm that the named property texture is actually present. This
|
|
// indicates that it is acceptable to pass the texture coordinate index to
|
|
// the material layer.
|
|
if (pName) {
|
|
result.propertyTextureIndices.Add(i);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
EncodedModelMetadata encodeModelMetadataAnyThreadPart(
|
|
const FCesiumModelMetadataDescription& metadataDescription,
|
|
const FCesiumModelMetadata& metadata) {
|
|
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::EncodeModelMetadata)
|
|
|
|
EncodedModelMetadata result;
|
|
|
|
const TArray<FCesiumPropertyTable>& propertyTables =
|
|
UCesiumModelMetadataBlueprintLibrary::GetPropertyTables(metadata);
|
|
result.propertyTables.Reserve(propertyTables.Num());
|
|
for (const FCesiumPropertyTable& propertyTable : propertyTables) {
|
|
const FString propertyTableName = getNameForPropertyTable(propertyTable);
|
|
|
|
const FCesiumPropertyTableDescription* pExpectedPropertyTable =
|
|
metadataDescription.PropertyTables.FindByPredicate(
|
|
[&propertyTableName](
|
|
const FCesiumPropertyTableDescription& expectedPropertyTable) {
|
|
return propertyTableName == expectedPropertyTable.Name;
|
|
});
|
|
|
|
if (pExpectedPropertyTable) {
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::EncodePropertyTable)
|
|
|
|
auto& encodedPropertyTable =
|
|
result.propertyTables.Emplace_GetRef(encodePropertyTableAnyThreadPart(
|
|
*pExpectedPropertyTable,
|
|
propertyTable));
|
|
encodedPropertyTable.name = propertyTableName;
|
|
}
|
|
}
|
|
|
|
const TArray<FCesiumPropertyTexture>& propertyTextures =
|
|
UCesiumModelMetadataBlueprintLibrary::GetPropertyTextures(metadata);
|
|
result.propertyTextures.Reserve(propertyTextures.Num());
|
|
|
|
TMap<const CesiumGltf::ImageAsset*, TWeakPtr<LoadedTextureResult>>
|
|
propertyTexturePropertyMap;
|
|
propertyTexturePropertyMap.Reserve(propertyTextures.Num());
|
|
|
|
for (const FCesiumPropertyTexture& propertyTexture : propertyTextures) {
|
|
FString propertyTextureName = getNameForPropertyTexture(propertyTexture);
|
|
|
|
const FCesiumPropertyTextureDescription* pExpectedPropertyTexture =
|
|
metadataDescription.PropertyTextures.FindByPredicate(
|
|
[&propertyTextureName](const FCesiumPropertyTextureDescription&
|
|
expectedPropertyTexture) {
|
|
return propertyTextureName == expectedPropertyTexture.Name;
|
|
});
|
|
|
|
if (pExpectedPropertyTexture) {
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::EncodePropertyTexture)
|
|
|
|
auto& encodedPropertyTexture = result.propertyTextures.Emplace_GetRef(
|
|
encodePropertyTextureAnyThreadPart(
|
|
*pExpectedPropertyTexture,
|
|
propertyTexture,
|
|
propertyTexturePropertyMap));
|
|
encodedPropertyTexture.name = propertyTextureName;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool encodePropertyTableGameThreadPart(
|
|
EncodedPropertyTable& encodedPropertyTable) {
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::EncodePropertyTable)
|
|
|
|
bool success = true;
|
|
|
|
for (EncodedPropertyTableProperty& encodedProperty :
|
|
encodedPropertyTable.properties) {
|
|
if (encodedProperty.pTexture) {
|
|
success &=
|
|
loadTextureGameThreadPart(encodedProperty.pTexture.Get()) != nullptr;
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
bool encodePropertyTextureGameThreadPart(
|
|
TArray<LoadedTextureResult*>& uniqueTextures,
|
|
EncodedPropertyTexture& encodedPropertyTexture) {
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::EncodePropertyTexture)
|
|
|
|
bool success = true;
|
|
|
|
for (EncodedPropertyTextureProperty& property :
|
|
encodedPropertyTexture.properties) {
|
|
if (uniqueTextures.Find(property.pTexture.Get()) == INDEX_NONE) {
|
|
success &= loadTextureGameThreadPart(property.pTexture.Get()) != nullptr;
|
|
uniqueTextures.Emplace(property.pTexture.Get());
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
bool encodeModelMetadataGameThreadPart(EncodedModelMetadata& encodedMetadata) {
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::EncodeMetadata)
|
|
|
|
bool success = true;
|
|
|
|
TArray<LoadedTextureResult*> uniqueTextures;
|
|
uniqueTextures.Reserve(encodedMetadata.propertyTextures.Num());
|
|
for (auto& encodedPropertyTextureIt : encodedMetadata.propertyTextures) {
|
|
success &= encodePropertyTextureGameThreadPart(
|
|
uniqueTextures,
|
|
encodedPropertyTextureIt);
|
|
}
|
|
|
|
for (auto& encodedPropertyTable : encodedMetadata.propertyTables) {
|
|
success &= encodePropertyTableGameThreadPart(encodedPropertyTable);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
void destroyEncodedModelMetadata(EncodedModelMetadata& encodedMetadata) {
|
|
for (auto& propertyTable : encodedMetadata.propertyTables) {
|
|
for (EncodedPropertyTableProperty& encodedProperty :
|
|
propertyTable.properties) {
|
|
if (encodedProperty.pTexture) {
|
|
encodedProperty.pTexture->pTexture = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto& encodedPropertyTextureIt : encodedMetadata.propertyTextures) {
|
|
for (EncodedPropertyTextureProperty& encodedPropertyTextureProperty :
|
|
encodedPropertyTextureIt.properties) {
|
|
if (encodedPropertyTextureProperty.pTexture) {
|
|
encodedPropertyTextureProperty.pTexture->pTexture = nullptr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// The result should be a safe hlsl identifier, but any name clashes
|
|
// after fixing safety will not be automatically handled.
|
|
FString createHlslSafeName(const FString& rawName) {
|
|
static const FString identifierHeadChar =
|
|
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";
|
|
static const FString identifierTailChar = identifierHeadChar + "0123456789";
|
|
|
|
FString safeName = rawName;
|
|
int32 _;
|
|
if (safeName.Len() == 0) {
|
|
return "_";
|
|
} else {
|
|
if (!identifierHeadChar.FindChar(safeName[0], _)) {
|
|
safeName = "_" + safeName;
|
|
}
|
|
}
|
|
|
|
for (size_t i = 1; i < safeName.Len(); ++i) {
|
|
if (!identifierTailChar.FindChar(safeName[i], _)) {
|
|
safeName[i] = '_';
|
|
}
|
|
}
|
|
|
|
return safeName;
|
|
}
|
|
|
|
// TODO: consider picking better pixel formats when they are available for the
|
|
// current platform.
|
|
EncodedPixelFormat getPixelFormat(
|
|
ECesiumEncodedMetadataType Type,
|
|
ECesiumEncodedMetadataComponentType ComponentType) {
|
|
switch (ComponentType) {
|
|
case ECesiumEncodedMetadataComponentType::Uint8:
|
|
switch (Type) {
|
|
case ECesiumEncodedMetadataType::Scalar:
|
|
return {EPixelFormat::PF_R8_UINT, 1, 1};
|
|
case ECesiumEncodedMetadataType::Vec2:
|
|
case ECesiumEncodedMetadataType::Vec3:
|
|
case ECesiumEncodedMetadataType::Vec4:
|
|
return {EPixelFormat::PF_R8G8B8A8_UINT, 1, 4};
|
|
default:
|
|
return {EPixelFormat::PF_Unknown, 0, 0};
|
|
}
|
|
case ECesiumEncodedMetadataComponentType::Float:
|
|
switch (Type) {
|
|
case ECesiumEncodedMetadataType::Scalar:
|
|
return {EPixelFormat::PF_R32_FLOAT, 4, 1};
|
|
case ECesiumEncodedMetadataType::Vec2:
|
|
case ECesiumEncodedMetadataType::Vec3:
|
|
case ECesiumEncodedMetadataType::Vec4:
|
|
// Note this is ABGR
|
|
return {EPixelFormat::PF_A32B32G32R32F, 4, 4};
|
|
}
|
|
default:
|
|
return {EPixelFormat::PF_Unknown, 0, 0};
|
|
}
|
|
}
|
|
|
|
bool isSupportedPropertyTextureProperty(
|
|
const FCesiumMetadataPropertyDetails& PropertyDetails) {
|
|
if (PropertyDetails.bIsArray &&
|
|
PropertyDetails.Type != ECesiumMetadataType::Scalar) {
|
|
// Only scalar arrays are supported.
|
|
return false;
|
|
}
|
|
|
|
uint32 byteSize = GetMetadataTypeByteSize(
|
|
PropertyDetails.Type,
|
|
PropertyDetails.ComponentType);
|
|
if (PropertyDetails.bIsArray) {
|
|
byteSize *= PropertyDetails.ArraySize;
|
|
}
|
|
|
|
return byteSize > 0 && byteSize <= 4;
|
|
}
|
|
|
|
void SetPropertyParameterValue(
|
|
UMaterialInstanceDynamic* pMaterial,
|
|
EMaterialParameterAssociation association,
|
|
int32 index,
|
|
const FString& name,
|
|
ECesiumEncodedMetadataType type,
|
|
const FCesiumMetadataValue& value,
|
|
float defaultValue) {
|
|
if (type == ECesiumEncodedMetadataType::Scalar) {
|
|
pMaterial->SetScalarParameterValueByInfo(
|
|
FMaterialParameterInfo(FName(name), association, index),
|
|
UCesiumMetadataValueBlueprintLibrary::GetFloat(value, defaultValue));
|
|
} else if (
|
|
type == ECesiumEncodedMetadataType::Vec2 ||
|
|
type == ECesiumEncodedMetadataType::Vec3 ||
|
|
type == ECesiumEncodedMetadataType::Vec4) {
|
|
FVector4 vector4Value = UCesiumMetadataValueBlueprintLibrary::GetVector4(
|
|
value,
|
|
FVector4(defaultValue, defaultValue, defaultValue, defaultValue));
|
|
|
|
pMaterial->SetVectorParameterValueByInfo(
|
|
FMaterialParameterInfo(FName(name), association, index),
|
|
FLinearColor(
|
|
static_cast<float>(vector4Value.X),
|
|
static_cast<float>(vector4Value.Y),
|
|
static_cast<float>(vector4Value.Z),
|
|
static_cast<float>(vector4Value.W)));
|
|
}
|
|
}
|
|
|
|
void SetFeatureIdTextureParameterValues(
|
|
UMaterialInstanceDynamic* pMaterial,
|
|
EMaterialParameterAssociation association,
|
|
int32 index,
|
|
const FString& name,
|
|
const EncodedFeatureIdTexture& encodedFeatureIdTexture) {
|
|
pMaterial->SetTextureParameterValueByInfo(
|
|
FMaterialParameterInfo(
|
|
FName(name + MaterialTextureSuffix),
|
|
association,
|
|
index),
|
|
encodedFeatureIdTexture.pTexture->pTexture->getUnrealTexture());
|
|
|
|
size_t numChannels = encodedFeatureIdTexture.channels.size();
|
|
pMaterial->SetScalarParameterValueByInfo(
|
|
FMaterialParameterInfo(
|
|
FName(name + MaterialNumChannelsSuffix),
|
|
association,
|
|
index),
|
|
static_cast<float>(numChannels));
|
|
|
|
std::vector<float> channelsAsFloats{0.0f, 0.0f, 0.0f, 0.0f};
|
|
for (size_t i = 0; i < numChannels; i++) {
|
|
channelsAsFloats[i] =
|
|
static_cast<float>(encodedFeatureIdTexture.channels[i]);
|
|
}
|
|
|
|
FLinearColor channels{
|
|
channelsAsFloats[0],
|
|
channelsAsFloats[1],
|
|
channelsAsFloats[2],
|
|
channelsAsFloats[3],
|
|
};
|
|
|
|
pMaterial->SetVectorParameterValueByInfo(
|
|
FMaterialParameterInfo(
|
|
FName(name + MaterialChannelsSuffix),
|
|
association,
|
|
index),
|
|
channels);
|
|
|
|
if (!encodedFeatureIdTexture.textureTransform) {
|
|
return;
|
|
}
|
|
|
|
glm::dvec2 scale = encodedFeatureIdTexture.textureTransform->scale();
|
|
glm::dvec2 offset = encodedFeatureIdTexture.textureTransform->offset();
|
|
|
|
pMaterial->SetVectorParameterValueByInfo(
|
|
FMaterialParameterInfo(
|
|
FName(name + MaterialTextureScaleOffsetSuffix),
|
|
association,
|
|
index),
|
|
FLinearColor(scale[0], scale[1], offset[0], offset[1]));
|
|
|
|
glm::dvec2 rotation =
|
|
encodedFeatureIdTexture.textureTransform->rotationSineCosine();
|
|
pMaterial->SetVectorParameterValueByInfo(
|
|
FMaterialParameterInfo(
|
|
FName(name + MaterialTextureRotationSuffix),
|
|
association,
|
|
index),
|
|
FLinearColor(rotation[0], rotation[1], 0.0f, 1.0f));
|
|
}
|
|
|
|
void SetPropertyTableParameterValues(
|
|
UMaterialInstanceDynamic* pMaterial,
|
|
EMaterialParameterAssociation association,
|
|
int32 index,
|
|
const EncodedPropertyTable& encodedPropertyTable) {
|
|
for (const EncodedPropertyTableProperty& encodedProperty :
|
|
encodedPropertyTable.properties) {
|
|
FString fullPropertyName = getMaterialNameForPropertyTableProperty(
|
|
encodedPropertyTable.name,
|
|
encodedProperty.name);
|
|
|
|
if (encodedProperty.pTexture) {
|
|
pMaterial->SetTextureParameterValueByInfo(
|
|
FMaterialParameterInfo(FName(fullPropertyName), association, index),
|
|
encodedProperty.pTexture->pTexture->getUnrealTexture());
|
|
}
|
|
|
|
if (!UCesiumMetadataValueBlueprintLibrary::IsEmpty(
|
|
encodedProperty.offset)) {
|
|
FString parameterName = fullPropertyName + MaterialPropertyOffsetSuffix;
|
|
SetPropertyParameterValue(
|
|
pMaterial,
|
|
association,
|
|
index,
|
|
parameterName,
|
|
encodedProperty.type,
|
|
encodedProperty.offset,
|
|
0.0f);
|
|
}
|
|
|
|
if (!UCesiumMetadataValueBlueprintLibrary::IsEmpty(encodedProperty.scale)) {
|
|
FString parameterName = fullPropertyName + MaterialPropertyScaleSuffix;
|
|
SetPropertyParameterValue(
|
|
pMaterial,
|
|
association,
|
|
index,
|
|
parameterName,
|
|
encodedProperty.type,
|
|
encodedProperty.scale,
|
|
1.0f);
|
|
}
|
|
|
|
if (!UCesiumMetadataValueBlueprintLibrary::IsEmpty(
|
|
encodedProperty.noData)) {
|
|
FString parameterName = fullPropertyName + MaterialPropertyNoDataSuffix;
|
|
SetPropertyParameterValue(
|
|
pMaterial,
|
|
association,
|
|
index,
|
|
parameterName,
|
|
encodedProperty.type,
|
|
encodedProperty.noData,
|
|
0.0f);
|
|
}
|
|
|
|
if (!UCesiumMetadataValueBlueprintLibrary::IsEmpty(
|
|
encodedProperty.defaultValue)) {
|
|
FString parameterName =
|
|
fullPropertyName + MaterialPropertyDefaultValueSuffix;
|
|
SetPropertyParameterValue(
|
|
pMaterial,
|
|
association,
|
|
index,
|
|
parameterName,
|
|
encodedProperty.type,
|
|
encodedProperty.defaultValue,
|
|
0.0f);
|
|
|
|
FString hasValueName = fullPropertyName + MaterialPropertyHasValueSuffix;
|
|
pMaterial->SetScalarParameterValueByInfo(
|
|
FMaterialParameterInfo(FName(hasValueName), association, index),
|
|
encodedProperty.pTexture ? 1.0 : 0.0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SetPropertyTextureParameterValues(
|
|
UMaterialInstanceDynamic* pMaterial,
|
|
EMaterialParameterAssociation association,
|
|
int32 index,
|
|
const EncodedPropertyTexture& encodedPropertyTexture) {
|
|
for (const EncodedPropertyTextureProperty& encodedProperty :
|
|
encodedPropertyTexture.properties) {
|
|
FString fullPropertyName = getMaterialNameForPropertyTextureProperty(
|
|
encodedPropertyTexture.name,
|
|
encodedProperty.name);
|
|
|
|
if (encodedProperty.pTexture) {
|
|
pMaterial->SetTextureParameterValueByInfo(
|
|
FMaterialParameterInfo(FName(fullPropertyName), association, index),
|
|
encodedProperty.pTexture->pTexture->getUnrealTexture());
|
|
}
|
|
|
|
pMaterial->SetVectorParameterValueByInfo(
|
|
FMaterialParameterInfo(
|
|
FName(fullPropertyName + MaterialChannelsSuffix),
|
|
association,
|
|
index),
|
|
FLinearColor(
|
|
encodedProperty.channels[0],
|
|
encodedProperty.channels[1],
|
|
encodedProperty.channels[2],
|
|
encodedProperty.channels[3]));
|
|
|
|
if (!UCesiumMetadataValueBlueprintLibrary::IsEmpty(
|
|
encodedProperty.offset)) {
|
|
FString parameterName = fullPropertyName + MaterialPropertyOffsetSuffix;
|
|
SetPropertyParameterValue(
|
|
pMaterial,
|
|
association,
|
|
index,
|
|
parameterName,
|
|
encodedProperty.type,
|
|
encodedProperty.offset,
|
|
0.0f);
|
|
}
|
|
|
|
if (!UCesiumMetadataValueBlueprintLibrary::IsEmpty(encodedProperty.scale)) {
|
|
FString parameterName = fullPropertyName + MaterialPropertyScaleSuffix;
|
|
SetPropertyParameterValue(
|
|
pMaterial,
|
|
association,
|
|
index,
|
|
parameterName,
|
|
encodedProperty.type,
|
|
encodedProperty.scale,
|
|
1.0f);
|
|
}
|
|
|
|
if (!UCesiumMetadataValueBlueprintLibrary::IsEmpty(
|
|
encodedProperty.noData)) {
|
|
FString parameterName = fullPropertyName + MaterialPropertyNoDataSuffix;
|
|
SetPropertyParameterValue(
|
|
pMaterial,
|
|
association,
|
|
index,
|
|
parameterName,
|
|
encodedProperty.type,
|
|
encodedProperty.noData,
|
|
0.0f);
|
|
}
|
|
|
|
if (!UCesiumMetadataValueBlueprintLibrary::IsEmpty(
|
|
encodedProperty.defaultValue)) {
|
|
FString parameterName =
|
|
fullPropertyName + MaterialPropertyDefaultValueSuffix;
|
|
SetPropertyParameterValue(
|
|
pMaterial,
|
|
association,
|
|
index,
|
|
parameterName,
|
|
encodedProperty.type,
|
|
encodedProperty.defaultValue,
|
|
0.0f);
|
|
|
|
FString hasValueName = fullPropertyName + MaterialPropertyHasValueSuffix;
|
|
pMaterial->SetScalarParameterValueByInfo(
|
|
FMaterialParameterInfo(FName(hasValueName), association, index),
|
|
encodedProperty.pTexture ? 1.0 : 0.0);
|
|
}
|
|
|
|
if (!encodedProperty.textureTransform) {
|
|
continue;
|
|
}
|
|
|
|
glm::dvec2 scale = encodedProperty.textureTransform->scale();
|
|
glm::dvec2 offset = encodedProperty.textureTransform->offset();
|
|
|
|
pMaterial->SetVectorParameterValueByInfo(
|
|
FMaterialParameterInfo(
|
|
FName(fullPropertyName + MaterialTextureScaleOffsetSuffix),
|
|
association,
|
|
index),
|
|
FLinearColor(scale[0], scale[1], offset[0], offset[1]));
|
|
|
|
glm::dvec2 rotation =
|
|
encodedProperty.textureTransform->rotationSineCosine();
|
|
pMaterial->SetVectorParameterValueByInfo(
|
|
FMaterialParameterInfo(
|
|
FName(fullPropertyName + MaterialTextureRotationSuffix),
|
|
association,
|
|
index),
|
|
FLinearColor(rotation[0], rotation[1], 0.0f, 1.0f));
|
|
}
|
|
}
|
|
|
|
} // namespace CesiumEncodedFeaturesMetadata
|