VRTowerDef/Plugins/MetaXR/Source/OculusXRPassthrough/Private/OculusXRPassthroughLayerCom...

648 lines
19 KiB
C++

// @lint-ignore-every LICENSELINT
// Copyright 1998-2020 Epic Games, Inc. All Rights Reserved.
#include "OculusXRPassthroughLayerComponent.h"
#include "Engine/StaticMesh.h"
#include "Components/StaticMeshComponent.h"
#include "ProceduralMeshComponent.h"
#include "OculusXRHMD.h"
#include "OculusXRPassthroughLayerShapes.h"
#include "Curves/CurveLinearColor.h"
#include "StaticMeshResources.h"
DEFINE_LOG_CATEGORY(LogOculusPassthrough);
void UOculusXRStereoLayerShapeReconstructed::ApplyShape(IStereoLayers::FLayerDesc& LayerDesc)
{
const FEdgeStyleParameters EdgeStyleParameters(
bEnableEdgeColor,
bEnableColorMap,
TextureOpacityFactor,
Brightness,
Contrast,
Posterize,
Saturation,
EdgeColor,
ColorScale,
ColorOffset,
ColorMapType,
GetColorArray(bUseColorMapCurve, ColorMapCurve),
GenerateColorLutDescription(LutWeight, ColorLUTSource, ColorLUTTarget));
LayerDesc.SetShape<FReconstructedLayer>(EdgeStyleParameters, LayerOrder);
}
void UOculusXRStereoLayerShapeUserDefined::ApplyShape(IStereoLayers::FLayerDesc& LayerDesc)
{
//If there is no user geometry, set the layer hidden to avoid unnecessary cost
if (UserGeometryList.IsEmpty())
LayerDesc.Flags |= IStereoLayers::LAYER_FLAG_HIDDEN;
const FEdgeStyleParameters EdgeStyleParameters(
bEnableEdgeColor,
bEnableColorMap,
TextureOpacityFactor,
Brightness,
Contrast,
Posterize,
Saturation,
EdgeColor,
ColorScale,
ColorOffset,
ColorMapType,
GetColorArray(bUseColorMapCurve, ColorMapCurve),
GenerateColorLutDescription(LutWeight, ColorLUTSource, ColorLUTTarget));
LayerDesc.SetShape<FUserDefinedLayer>(UserGeometryList, EdgeStyleParameters, LayerOrder);
}
void UOculusXRStereoLayerShapeUserDefined::AddGeometry(const FString& MeshName, OculusXRHMD::FOculusPassthroughMeshRef PassthroughMesh, FTransform Transform, bool bUpdateTransform)
{
FUserDefinedGeometryDesc UserDefinedGeometryDesc(
MeshName,
PassthroughMesh,
Transform,
bUpdateTransform);
UserGeometryList.Add(UserDefinedGeometryDesc);
}
void UOculusXRStereoLayerShapeUserDefined::RemoveGeometry(const FString& MeshName)
{
UserGeometryList.RemoveAll([MeshName](const FUserDefinedGeometryDesc& Desc) {
return Desc.MeshName == MeshName;
});
}
UOculusXRPassthroughLayerComponent::UOculusXRPassthroughLayerComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
void UOculusXRPassthroughLayerComponent::DestroyComponent(bool bPromoteChildren)
{
Super::DestroyComponent(bPromoteChildren);
#ifdef WITH_OCULUS_BRANCH
IStereoLayers* StereoLayers;
if (LayerId && GEngine->StereoRenderingDevice.IsValid() && (StereoLayers = GEngine->StereoRenderingDevice->GetStereoLayers()) != nullptr)
{
StereoLayers->DestroyLayer(LayerId);
LayerId = 0;
}
#endif
}
void UOculusXRPassthroughLayerComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
#ifndef WITH_OCULUS_BRANCH
if (Texture == nullptr && !LayerRequiresTexture())
{
// UStereoLayerComponent hides components without textures
Texture = GEngine->DefaultTexture;
}
#endif
UpdatePassthroughObjects();
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
}
void UOculusXRPassthroughLayerComponent::UpdatePassthroughObjects()
{
UOculusXRStereoLayerShapeUserDefined* UserShape = Cast<UOculusXRStereoLayerShapeUserDefined>(Shape);
if (UserShape)
{
bool bDirty = false;
for (FUserDefinedGeometryDesc& Entry : UserShape->GetUserGeometryList())
{
if (Entry.bUpdateTransform)
{
const UMeshComponent** MeshComponent = PassthroughComponentMap.Find(Entry.MeshName);
if (MeshComponent)
{
Entry.Transform = (*MeshComponent)->GetComponentTransform();
bDirty = true;
}
}
}
if (bDirty)
{
MarkStereoLayerDirty();
}
}
}
OculusXRHMD::FOculusPassthroughMeshRef UOculusXRPassthroughLayerComponent::CreatePassthroughMesh(UProceduralMeshComponent* ProceduralMeshComponent)
{
if (!ProceduralMeshComponent)
{
UE_LOG(LogOculusPassthrough, Error, TEXT("Passthrough Procedural Mesh is nullptr"));
return nullptr;
}
TArray<int32> Triangles;
TArray<FVector> Vertices;
int32 NumSections = ProceduralMeshComponent->GetNumSections();
int VertexOffset = 0; //Each section start with vertex IDs of 0, in order to create a single mesh from all sections we need to offset those IDs by the amount of previous vertices
for (int32 s = 0; s < NumSections; ++s)
{
FProcMeshSection* ProcMeshSection = ProceduralMeshComponent->GetProcMeshSection(s);
for (int32 i = 0; i < ProcMeshSection->ProcIndexBuffer.Num(); ++i)
{
Triangles.Add(VertexOffset + ProcMeshSection->ProcIndexBuffer[i]);
}
for (int32 i = 0; i < ProcMeshSection->ProcVertexBuffer.Num(); ++i)
{
Vertices.Add(ProcMeshSection->ProcVertexBuffer[i].Position);
}
VertexOffset += ProcMeshSection->ProcVertexBuffer.Num();
}
OculusXRHMD::FOculusPassthroughMeshRef PassthroughMesh = new OculusXRHMD::FOculusPassthroughMesh(Vertices, Triangles);
return PassthroughMesh;
}
OculusXRHMD::FOculusPassthroughMeshRef UOculusXRPassthroughLayerComponent::CreatePassthroughMesh(UStaticMeshComponent* StaticMeshComponent)
{
if (!StaticMeshComponent)
{
UE_LOG(LogOculusPassthrough, Error, TEXT("Passthrough Static Mesh is nullptr"));
return nullptr;
}
UStaticMesh* Mesh = StaticMeshComponent->GetStaticMesh();
if (!Mesh || !Mesh->GetRenderData())
{
UE_LOG(LogOculusPassthrough, Error, TEXT("Passthrough Static Mesh has no Renderdata"));
return nullptr;
}
if (Mesh->GetNumLODs() == 0)
{
UE_LOG(LogOculusPassthrough, Error, TEXT("Passthrough Static Mesh has no LODs"));
return nullptr;
}
if (!Mesh->bAllowCPUAccess)
{
UE_LOG(LogOculusPassthrough, Error, TEXT("Passthrough Static Mesh Requires CPU Access"));
return nullptr;
}
const int32 LODIndex = 0;
FStaticMeshLODResources& LOD = Mesh->GetRenderData()->LODResources[LODIndex];
TArray<int32> Triangles;
const int32 NumIndices = LOD.IndexBuffer.GetNumIndices();
for (int32 i = 0; i < NumIndices; ++i)
{
Triangles.Add(LOD.IndexBuffer.GetIndex(i));
}
TArray<FVector> Vertices;
const int32 NumVertices = LOD.VertexBuffers.PositionVertexBuffer.GetNumVertices();
for (int32 i = 0; i < NumVertices; ++i)
{
Vertices.Add((FVector)LOD.VertexBuffers.PositionVertexBuffer.VertexPosition(i));
}
OculusXRHMD::FOculusPassthroughMeshRef PassthroughMesh = new OculusXRHMD::FOculusPassthroughMesh(Vertices, Triangles);
return PassthroughMesh;
}
void UOculusXRPassthroughLayerComponent::AddSurfaceGeometry(AStaticMeshActor* StaticMeshActor, bool updateTransform)
{
if (StaticMeshActor)
{
UStaticMeshComponent* StaticMeshComponent = StaticMeshActor->GetStaticMeshComponent();
if (StaticMeshComponent)
AddStaticSurfaceGeometry(StaticMeshComponent, updateTransform);
}
}
void UOculusXRPassthroughLayerComponent::AddStaticSurfaceGeometry(UStaticMeshComponent* StaticMeshComponent, bool updateTransform)
{
if (!StaticMeshComponent)
return;
UOculusXRStereoLayerShapeUserDefined* UserShape = Cast<UOculusXRStereoLayerShapeUserDefined>(Shape);
if (!UserShape)
return;
OculusXRHMD::FOculusPassthroughMeshRef PassthroughMesh = CreatePassthroughMesh(StaticMeshComponent);
if (!PassthroughMesh)
return;
const FString MeshName = StaticMeshComponent->GetFullName();
const FTransform Transform = StaticMeshComponent->GetComponentTransform();
UserShape->AddGeometry(MeshName, PassthroughMesh, Transform, updateTransform);
PassthroughComponentMap.Add(MeshName, StaticMeshComponent);
MarkStereoLayerDirty();
}
void UOculusXRPassthroughLayerComponent::AddProceduralSurfaceGeometry(UProceduralMeshComponent* ProceduralMeshComponent, bool updateTransform)
{
if (!ProceduralMeshComponent)
return;
UOculusXRStereoLayerShapeUserDefined* UserShape = Cast<UOculusXRStereoLayerShapeUserDefined>(Shape);
if (!UserShape)
return;
OculusXRHMD::FOculusPassthroughMeshRef PassthroughMesh = CreatePassthroughMesh(ProceduralMeshComponent);
if (!PassthroughMesh)
return;
const FString MeshName = ProceduralMeshComponent->GetFullName();
const FTransform Transform = ProceduralMeshComponent->GetComponentTransform();
UserShape->AddGeometry(MeshName, PassthroughMesh, Transform, updateTransform);
PassthroughComponentMap.Add(MeshName, ProceduralMeshComponent);
MarkStereoLayerDirty();
}
void UOculusXRPassthroughLayerComponent::RemoveSurfaceGeometry(AStaticMeshActor* StaticMeshActor)
{
if (StaticMeshActor)
RemoveSurfaceGeometryComponent(StaticMeshActor->GetStaticMeshComponent());
}
void UOculusXRPassthroughLayerComponent::RemoveStaticSurfaceGeometry(UStaticMeshComponent* StaticMeshComponent)
{
RemoveSurfaceGeometryComponent(StaticMeshComponent);
}
void UOculusXRPassthroughLayerComponent::RemoveProceduralSurfaceGeometry(UProceduralMeshComponent* ProceduralMeshComponent)
{
RemoveSurfaceGeometryComponent(ProceduralMeshComponent);
}
void UOculusXRPassthroughLayerComponent::RemoveSurfaceGeometryComponent(UMeshComponent* MeshComponent)
{
if (!MeshComponent)
return;
UOculusXRStereoLayerShapeUserDefined* UserShape = Cast<UOculusXRStereoLayerShapeUserDefined>(Shape);
if (!UserShape)
return;
const FString MeshName = MeshComponent->GetFullName();
UserShape->RemoveGeometry(MeshName);
PassthroughComponentMap.Remove(MeshName);
MarkStereoLayerDirty();
}
bool UOculusXRPassthroughLayerComponent::IsSurfaceGeometry(AStaticMeshActor* StaticMeshActor) const
{
return StaticMeshActor ? IsSurfaceGeometryComponent(StaticMeshActor->GetStaticMeshComponent()) : false;
}
bool UOculusXRPassthroughLayerComponent::IsSurfaceGeometryComponent(const UMeshComponent* MeshComponent) const
{
return MeshComponent ? PassthroughComponentMap.Contains(MeshComponent->GetFullName()) : false;
}
void UOculusXRPassthroughLayerComponent::MarkPassthroughStyleForUpdate()
{
bPassthroughStyleNeedsUpdate = true;
}
#if WITH_EDITOR
bool UOculusXRPassthroughLayerComponent::CanEditChange(const FProperty* InProperty) const
{
if (!Super::CanEditChange(InProperty))
return false;
if (!(Shape && Shape.IsA(UOculusXRPassthroughLayerBase::StaticClass())))
{
return true;
}
const FName PropertyName = InProperty->GetFName();
if (PropertyName == GET_MEMBER_NAME_CHECKED(UOculusXRPassthroughLayerComponent, Texture)
|| PropertyName == GET_MEMBER_NAME_CHECKED(UOculusXRPassthroughLayerComponent, bQuadPreserveTextureRatio)
|| PropertyName == GET_MEMBER_NAME_CHECKED(UOculusXRPassthroughLayerComponent, QuadSize)
|| PropertyName == GET_MEMBER_NAME_CHECKED(UOculusXRPassthroughLayerComponent, UVRect)
|| PropertyName == GET_MEMBER_NAME_CHECKED(UOculusXRPassthroughLayerComponent, StereoLayerType))
{
return false;
}
return true;
}
#endif // WITH_EDITOR
bool UOculusXRPassthroughLayerComponent::LayerRequiresTexture()
{
const bool bIsPassthroughShape = Shape && (Shape->IsA<UOculusXRStereoLayerShapeReconstructed>() || Shape->IsA<UOculusXRStereoLayerShapeUserDefined>());
return !bIsPassthroughShape;
}
void UOculusXRPassthroughLayerBase::SetTextureOpacity(float InOpacity)
{
if (TextureOpacityFactor == InOpacity)
{
return;
}
TextureOpacityFactor = InOpacity;
MarkStereoLayerDirty();
}
void UOculusXRPassthroughLayerBase::EnableEdgeColor(bool bInEnableEdgeColor)
{
if (bEnableEdgeColor == bInEnableEdgeColor)
{
return;
}
bEnableEdgeColor = bInEnableEdgeColor;
MarkStereoLayerDirty();
}
void UOculusXRPassthroughLayerBase::EnableColorMap(bool bInEnableColorMap)
{
if (bEnableColorMap == bInEnableColorMap)
{
return;
}
bEnableColorMap = bInEnableColorMap;
MarkStereoLayerDirty();
}
void UOculusXRPassthroughLayerBase::SetEdgeRenderingColor(FLinearColor InEdgeColor)
{
if (EdgeColor == InEdgeColor)
{
return;
}
EdgeColor = InEdgeColor;
MarkStereoLayerDirty();
}
void UOculusXRPassthroughLayerBase::EnableColorMapCurve(bool bInEnableColorMapCurve)
{
if (bUseColorMapCurve == bInEnableColorMapCurve)
{
return;
}
bUseColorMapCurve = bInEnableColorMapCurve;
ColorArray = GenerateColorArray(bUseColorMapCurve, ColorMapCurve);
MarkStereoLayerDirty();
}
void UOculusXRPassthroughLayerBase::SetColorMapCurve(UCurveLinearColor* InColorMapCurve)
{
if (ColorMapCurve == InColorMapCurve)
{
return;
}
ColorMapCurve = InColorMapCurve;
ColorArray = GenerateColorArray(bUseColorMapCurve, ColorMapCurve);
MarkStereoLayerDirty();
}
void UOculusXRPassthroughLayerBase::SetColorMapType(EOculusXRColorMapType InColorMapType)
{
if (ColorMapType == InColorMapType)
{
return;
}
ColorMapType = InColorMapType;
ColorArray = GenerateColorArray(bUseColorMapCurve, ColorMapCurve);
MarkStereoLayerDirty();
}
void UOculusXRPassthroughLayerBase::SetColorArray(const TArray<FLinearColor>& InColorArray)
{
if (InColorArray.Num() == 0)
{
return;
}
if (ColorMapType != ColorMapType_GrayscaleToColor)
{
UE_LOG(LogOculusPassthrough, Warning, TEXT("SetColorArray is ignored for color map types other than Grayscale to Color."));
return;
}
if (bUseColorMapCurve)
{
UE_LOG(LogOculusPassthrough, Warning, TEXT("UseColorMapCurve is enabled on the layer. Automatic disable and use the Array for color lookup"));
}
bUseColorMapCurve = false;
ColorArray = InColorArray;
MarkStereoLayerDirty();
}
void UOculusXRPassthroughLayerBase::ClearColorMap()
{
ColorArray.Empty();
}
void UOculusXRPassthroughLayerBase::SetColorMapControls(float InContrast, float InBrightness, float InPosterize)
{
if (ColorMapType != ColorMapType_Grayscale && ColorMapType != ColorMapType_GrayscaleToColor)
{
UE_LOG(LogOculusPassthrough, Warning, TEXT("SetColorMapControls is ignored for color map types other than Grayscale and Grayscale to color."));
return;
}
Contrast = FMath::Clamp(InContrast, -1.0f, 1.0f);
Brightness = FMath::Clamp(InBrightness, -1.0f, 1.0f);
Posterize = FMath::Clamp(InPosterize, 0.0f, 1.0f);
MarkStereoLayerDirty();
}
void UOculusXRPassthroughLayerBase::SetBrightnessContrastSaturation(float InContrast, float InBrightness, float InSaturation)
{
if (ColorMapType != ColorMapType_ColorAdjustment)
{
UE_LOG(LogOculusPassthrough, Warning, TEXT("SetBrightnessContrastSaturation is ignored for color map types other than Color Adjustment."));
return;
}
Contrast = FMath::Clamp(InContrast, -1.0f, 1.0f);
Brightness = FMath::Clamp(InBrightness, -1.0f, 1.0f);
Saturation = FMath::Clamp(InSaturation, -1.0f, 1.0f);
MarkStereoLayerDirty();
}
void UOculusXRPassthroughLayerBase::SetColorScaleAndOffset(FLinearColor InColorScale, FLinearColor InColorOffset)
{
if (ColorScale == InColorScale && ColorOffset == InColorOffset)
{
return;
}
ColorScale = InColorScale;
ColorOffset = InColorOffset;
MarkStereoLayerDirty();
}
void UOculusXRPassthroughLayerBase::SetLayerPlacement(EOculusXRPassthroughLayerOrder InLayerOrder)
{
if (LayerOrder == InLayerOrder)
{
UE_LOG(LogOculusPassthrough, Warning, TEXT("Same layer order as before, no change needed"));
return;
}
LayerOrder = InLayerOrder;
this->MarkStereoLayerDirty();
}
void UOculusXRPassthroughLayerBase::SetColorLUTSource(class UOculusXRPassthroughColorLut* InColorLUTSource)
{
if (ColorMapType != ColorMapType_ColorLut && ColorMapType != ColorMapType_ColorLut_Interpolated)
{
UE_LOG(LogOculusPassthrough, Warning, TEXT("SetColorLUT is ignored for color map types other than Color LUT."));
return;
}
if (InColorLUTSource == ColorLUTSource)
{
UE_LOG(LogOculusPassthrough, Warning, TEXT("Same color LUT source as before, no change needed"));
return;
}
ColorLUTSource = InColorLUTSource;
MarkStereoLayerDirty();
}
void UOculusXRPassthroughLayerBase::SetColorLUTTarget(class UOculusXRPassthroughColorLut* InColorLUTTarget)
{
if (ColorMapType != ColorMapType_ColorLut_Interpolated)
{
UE_LOG(LogOculusPassthrough, Warning, TEXT("SetColorLUTTarget is ignored for color map types other than Interpolated Color LUT."));
return;
}
if (InColorLUTTarget == ColorLUTTarget)
{
UE_LOG(LogOculusPassthrough, Warning, TEXT("Same color LUT source as before, no change needed"));
return;
}
ColorLUTTarget = InColorLUTTarget;
MarkStereoLayerDirty();
}
void UOculusXRPassthroughLayerBase::SetColorLUTWeight(float InWeight)
{
if (ColorMapType != ColorMapType_ColorLut && ColorMapType != ColorMapType_ColorLut_Interpolated)
{
UE_LOG(LogOculusPassthrough, Warning, TEXT("SetWeight is ignored for color map types other than Color LUT."));
return;
}
if (LutWeight == InWeight)
{
UE_LOG(LogOculusPassthrough, Warning, TEXT("Same lut weight as before, no change needed"));
return;
}
LutWeight = InWeight;
MarkStereoLayerDirty();
}
void UOculusXRPassthroughLayerBase::RemoveColorLut()
{
ColorLUTSource = nullptr;
ColorLUTTarget = nullptr;
MarkStereoLayerDirty();
}
TArray<FLinearColor> UOculusXRPassthroughLayerBase::GenerateColorArrayFromColorCurve(const UCurveLinearColor* InColorMapCurve) const
{
if (InColorMapCurve == nullptr)
{
return TArray<FLinearColor>();
}
TArray<FLinearColor> NewColorArray;
constexpr uint32 TotalEntries = 256;
NewColorArray.Empty();
NewColorArray.SetNum(TotalEntries);
for (int32 Index = 0; Index < TotalEntries; ++Index)
{
const float Alpha = ((float)Index / TotalEntries);
NewColorArray[Index] = InColorMapCurve->GetLinearColorValue(Alpha);
}
return NewColorArray;
}
TArray<FLinearColor> UOculusXRPassthroughLayerBase::GetOrGenerateNeutralColorArray()
{
if (NeutralColorArray.Num() == 0)
{
const uint32 TotalEntries = 256;
NeutralColorArray.SetNum(TotalEntries);
for (int32 Index = 0; Index < TotalEntries; ++Index)
{
NeutralColorArray[Index] = FLinearColor((float)Index / TotalEntries, (float)Index / TotalEntries, (float)Index / TotalEntries);
}
}
return NeutralColorArray;
}
TArray<FLinearColor> UOculusXRPassthroughLayerBase::GenerateColorArray(bool bInUseColorMapCurve, const UCurveLinearColor* InColorMapCurve)
{
TArray<FLinearColor> NewColorArray;
if (bInUseColorMapCurve)
{
NewColorArray = GenerateColorArrayFromColorCurve(InColorMapCurve);
}
// Check for existing Array, otherwise generate a neutral one
if (NewColorArray.Num() == 0)
{
NewColorArray = GetOrGenerateNeutralColorArray();
}
return NewColorArray;
}
TArray<FLinearColor> UOculusXRPassthroughLayerBase::GetColorArray(bool bInUseColorMapCurve, const UCurveLinearColor* InColorMapCurve)
{
if (ColorArray.Num() == 0)
{
if (bInUseColorMapCurve)
{
return GenerateColorArray(bInUseColorMapCurve, InColorMapCurve);
}
return GetOrGenerateNeutralColorArray();
}
return ColorArray;
}
FColorLutDesc UOculusXRPassthroughLayerBase::GenerateColorLutDescription(float InLutWeight, UOculusXRPassthroughColorLut* InLutSource, UOculusXRPassthroughColorLut* InLutTarget)
{
TArray<uint64> ColorLuts;
if (InLutSource != nullptr && InLutSource->ColorLutType != EColorLutType::None)
{
uint64 ColorLutHandle = InLutSource->GetHandle();
if (ColorLutHandle != 0)
{
ColorLuts.Add(ColorLutHandle);
}
}
if (ColorMapType == EOculusXRColorMapType::ColorMapType_ColorLut_Interpolated && ColorLuts.Num() > 0 && InLutSource->ColorLutType != EColorLutType::None)
{
uint64 ColorLutHandle = InLutTarget->GetHandle();
if (ColorLutHandle != 0)
{
ColorLuts.Add(ColorLutHandle);
}
}
return FColorLutDesc(ColorLuts, InLutWeight);
}