Started the project... finally...
Started the project. This right now only includes Meta XR and Android setup with VR template. More improvements to come!
This commit is contained in:
@@ -0,0 +1,278 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRPassthroughColorLut.h"
|
||||
#include "OculusXRPassthroughLayerComponent.h"
|
||||
#include "OculusXRHMDPrivate.h"
|
||||
#include "Math/UnrealMathUtility.h"
|
||||
#include "GenericPlatform/GenericPlatformMath.h"
|
||||
#include "UObject/ObjectSaveContext.h"
|
||||
#include "OculusXRHMD.h"
|
||||
#include "TextureResource.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
ovrpPassthroughColorLutChannels ToOVRPColorLutChannels(EColorLutChannels InColorLutChannels)
|
||||
{
|
||||
switch (InColorLutChannels)
|
||||
{
|
||||
case ColorLutChannels_RGB:
|
||||
return ovrpPassthroughColorLutChannels_Rgb;
|
||||
case ColorLutChannels_RGBA:
|
||||
return ovrpPassthroughColorLutChannels_Rgba;
|
||||
default:
|
||||
return ovrpPassthroughColorLutChannels_Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
TArray<uint8> ColorArrayToColorData(const TArray<FColor>& InColorArray, bool IgnoreAlphaChannel)
|
||||
{
|
||||
TArray<uint8> Data;
|
||||
const size_t ElementSize = IgnoreAlphaChannel ? 3 : 4;
|
||||
Data.SetNum(InColorArray.Num() * ElementSize);
|
||||
uint8* Dest = Data.GetData();
|
||||
for (size_t i = 0; i < InColorArray.Num(); i++)
|
||||
{
|
||||
Data[i * ElementSize + 0] = InColorArray[i].R;
|
||||
Data[i * ElementSize + 1] = InColorArray[i].G;
|
||||
Data[i * ElementSize + 2] = InColorArray[i].B;
|
||||
|
||||
if (!IgnoreAlphaChannel)
|
||||
{
|
||||
Data[i * ElementSize + 3] = InColorArray[i].A;
|
||||
}
|
||||
}
|
||||
|
||||
return Data;
|
||||
}
|
||||
|
||||
bool IsTextureDataValid(const FLutTextureData& Data)
|
||||
{
|
||||
return Data.Data.Num() > 0 && Data.Resolution > 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void UOculusXRPassthroughColorLut::SetLutFromArray(const TArray<FColor>& InColorArray, bool InIgnoreAlphaChannel)
|
||||
{
|
||||
const int32 Size = InColorArray.Num();
|
||||
const int32 Resolution = FPlatformMath::RoundToInt(FPlatformMath::Pow(Size, 1.0 / 3));
|
||||
if (Resolution > GetMaxResolution())
|
||||
{
|
||||
UE_LOG(LogOculusPassthrough, Warning, TEXT("Setting array ignored: Resoluton is exceeding maximum resoluton of %d."), GetMaxResolution());
|
||||
return;
|
||||
}
|
||||
if (Resolution * Resolution * Resolution != Size)
|
||||
{
|
||||
UE_LOG(LogOculusPassthrough, Warning, TEXT("Setting array ignored: Provided array size is not cube."));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if size if power of 2 */
|
||||
if ((Size & (Size - 1)) != 0)
|
||||
{
|
||||
UE_LOG(LogOculusPassthrough, Warning, TEXT("Setting array ignored: Provided array does not result in a resolution that is a power of two."));
|
||||
return;
|
||||
}
|
||||
|
||||
ColorLutType = EColorLutType::Array;
|
||||
|
||||
const TArray<uint8>& Data = ColorArrayToColorData(InColorArray, InIgnoreAlphaChannel);
|
||||
|
||||
if (LutHandle == 0)
|
||||
{
|
||||
LutHandle = CreateLutObject(Data, Resolution);
|
||||
return;
|
||||
}
|
||||
|
||||
if (InIgnoreAlphaChannel == IgnoreAlphaChannel && Resolution == ColorArrayResolution)
|
||||
{
|
||||
UpdateLutObject(LutHandle, Data);
|
||||
return;
|
||||
}
|
||||
|
||||
DestroyLutObject(LutHandle);
|
||||
LutHandle = CreateLutObject(Data, Resolution);
|
||||
|
||||
IgnoreAlphaChannel = InIgnoreAlphaChannel;
|
||||
ColorArrayResolution = Resolution;
|
||||
}
|
||||
|
||||
uint64 UOculusXRPassthroughColorLut::GetHandle()
|
||||
{
|
||||
if (LutHandle == 0 && ColorLutType == EColorLutType::TextureLUT && IsTextureDataValid(StoredTextureData))
|
||||
{
|
||||
LutHandle = CreateLutObject(StoredTextureData.Data, StoredTextureData.Resolution);
|
||||
}
|
||||
|
||||
return LutHandle;
|
||||
}
|
||||
|
||||
void UOculusXRPassthroughColorLut::PreSave(FObjectPreSaveContext ObjectSaveContext)
|
||||
{
|
||||
Super::PreSave(ObjectSaveContext);
|
||||
#if WITH_EDITOR
|
||||
StoredTextureData = TextureToColorData(LutTexture);
|
||||
#endif
|
||||
}
|
||||
|
||||
FLutTextureData UOculusXRPassthroughColorLut::TextureToColorData(class UTexture2D* InLutTexture) const
|
||||
{
|
||||
|
||||
if (ColorLutType != EColorLutType::TextureLUT)
|
||||
{
|
||||
return FLutTextureData();
|
||||
}
|
||||
|
||||
if (InLutTexture == nullptr)
|
||||
{
|
||||
UE_LOG(LogOculusPassthrough, Warning, TEXT("Ignoring provided LUT texture. Provided texture is NULL."));
|
||||
return FLutTextureData();
|
||||
}
|
||||
|
||||
if (InLutTexture->LODGroup != TextureGroup::TEXTUREGROUP_ColorLookupTable)
|
||||
{
|
||||
UE_LOG(LogOculusPassthrough, Warning, TEXT("Ignoring provided LUT texture. Provided texture is not LUT texture."));
|
||||
return FLutTextureData();
|
||||
}
|
||||
|
||||
if (InLutTexture->GetPlatformData()->Mips.Num() <= 0)
|
||||
{
|
||||
if (IsTextureDataValid(StoredTextureData))
|
||||
{
|
||||
// We do not need to save it again. Use previously saved data.
|
||||
return StoredTextureData;
|
||||
}
|
||||
return FLutTextureData();
|
||||
}
|
||||
|
||||
const uint32 TextureWidth = InLutTexture->GetImportedSize().X;
|
||||
const uint32 TextureHeight = InLutTexture->GetImportedSize().Y;
|
||||
|
||||
uint32 ColorMapSize;
|
||||
uint32 SlicesPerRow;
|
||||
|
||||
if (TextureWidth == TextureHeight)
|
||||
{
|
||||
float EdgeLength = FPlatformMath::Pow(TextureWidth, 2.0f / 3.0f);
|
||||
ColorMapSize = FPlatformMath::RoundToInt(EdgeLength);
|
||||
if (FPlatformMath::Abs(EdgeLength - ColorMapSize) > ZERO_ANIMWEIGHT_THRESH)
|
||||
{
|
||||
UE_LOG(LogOculusPassthrough, Warning, TEXT("LUT width and height are equal but don't correspond to an 'exploded cube'"));
|
||||
return FLutTextureData();
|
||||
}
|
||||
|
||||
SlicesPerRow = FPlatformMath::Sqrt(ColorMapSize * 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TextureWidth != TextureHeight * TextureHeight)
|
||||
{
|
||||
UE_LOG(LogOculusPassthrough, Warning, TEXT("For rectangular LUTs, the width is expected to be equal to edgeLength^2"));
|
||||
return FLutTextureData();
|
||||
}
|
||||
ColorMapSize = TextureHeight;
|
||||
SlicesPerRow = TextureHeight;
|
||||
}
|
||||
|
||||
FTexture2DMipMap& MipMap = InLutTexture->GetPlatformData()->Mips[0];
|
||||
FByteBulkData* BulkData = &MipMap.BulkData;
|
||||
const FColor* FormatedImageData = reinterpret_cast<const FColor*>(BulkData->Lock(LOCK_READ_ONLY));
|
||||
|
||||
TArray<FColor> Colors;
|
||||
Colors.SetNum(ColorMapSize * ColorMapSize * ColorMapSize);
|
||||
|
||||
for (uint32 bi = 0; bi < ColorMapSize; bi++)
|
||||
{
|
||||
uint32 bi_row = bi % SlicesPerRow;
|
||||
uint32 bi_col = bi / SlicesPerRow;
|
||||
for (uint32 gi = 0; gi < ColorMapSize; gi++)
|
||||
{
|
||||
for (uint32 ri = 0; ri < ColorMapSize; ri++)
|
||||
{
|
||||
uint32 sX = ri + bi_row * ColorMapSize;
|
||||
uint32 sY = gi + bi_col * ColorMapSize;
|
||||
Colors[bi * ColorMapSize * ColorMapSize + gi * ColorMapSize + ri] = FormatedImageData[sX + sY * TextureWidth];
|
||||
}
|
||||
}
|
||||
}
|
||||
BulkData->Unlock();
|
||||
return FLutTextureData(ColorArrayToColorData(Colors, IgnoreAlphaChannel), ColorMapSize);
|
||||
}
|
||||
|
||||
uint64 UOculusXRPassthroughColorLut::CreateLutObject(const TArray<uint8>& InData, uint32 Resolution) const
|
||||
{
|
||||
ovrpPassthroughColorLutData OVRPData;
|
||||
OVRPData.Buffer = InData.GetData();
|
||||
OVRPData.BufferSize = InData.Num();
|
||||
const EColorLutChannels Channels = IgnoreAlphaChannel ? EColorLutChannels::ColorLutChannels_RGB : EColorLutChannels::ColorLutChannels_RGBA;
|
||||
ovrpPassthroughColorLut Handle;
|
||||
if (OVRP_FAILURE(FOculusXRHMDModule::GetPluginWrapper().CreatePassthroughColorLut(
|
||||
ToOVRPColorLutChannels(Channels),
|
||||
Resolution,
|
||||
OVRPData,
|
||||
&Handle)))
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("Failed creating passthrough color lut."));
|
||||
return 0;
|
||||
}
|
||||
return Handle;
|
||||
}
|
||||
|
||||
void UOculusXRPassthroughColorLut::UpdateLutObject(uint64 Handle, const TArray<uint8>& InData) const
|
||||
{
|
||||
if (Handle == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ovrpPassthroughColorLutData OVRPData;
|
||||
OVRPData.Buffer = InData.GetData();
|
||||
OVRPData.BufferSize = InData.Num();
|
||||
|
||||
if (OVRP_FAILURE(FOculusXRHMDModule::GetPluginWrapper().UpdatePassthroughColorLut(
|
||||
Handle,
|
||||
OVRPData)))
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("Failed updating passthrough color lut data."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void UOculusXRPassthroughColorLut::DestroyLutObject(uint64 Handle) const
|
||||
{
|
||||
if (Handle == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (OVRP_FAILURE(FOculusXRHMDModule::GetPluginWrapper().DestroyPassthroughColorLut(Handle)))
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("Failed to destroy passthrough color lut."));
|
||||
}
|
||||
}
|
||||
|
||||
void UOculusXRPassthroughColorLut::BeginDestroy()
|
||||
{
|
||||
Super::BeginDestroy();
|
||||
DestroyLutObject(LutHandle);
|
||||
}
|
||||
|
||||
int UOculusXRPassthroughColorLut::GetMaxResolution()
|
||||
{
|
||||
if (MaxResolution > -1)
|
||||
{
|
||||
return MaxResolution;
|
||||
}
|
||||
|
||||
ovrpInsightPassthroughCapabilities PassthroughCapabilites;
|
||||
PassthroughCapabilites.Fields =
|
||||
static_cast<ovrpInsightPassthroughCapabilityFields>(
|
||||
ovrpInsightPassthroughCapabilityFields::ovrpInsightPassthroughCapabilityFields_Flags | ovrpInsightPassthroughCapabilityFields::ovrpInsightPassthroughCapabilityFields_MaxColorLutResolution);
|
||||
|
||||
if (OVRP_FAILURE(FOculusXRHMDModule::GetPluginWrapper().GetPassthroughCapabilities(&PassthroughCapabilites)))
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("Failed to fetch passthrough capabilities."));
|
||||
// Default MAX resoulution is 64.
|
||||
return 64;
|
||||
}
|
||||
MaxResolution = PassthroughCapabilites.MaxColorLutResolution;
|
||||
return MaxResolution;
|
||||
}
|
||||
@@ -0,0 +1,647 @@
|
||||
// @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);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRPassthroughModule.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "OculusXRPassthrough"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// FOculusXRPassthroughModule
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
FOculusXRPassthroughModule::FOculusXRPassthroughModule()
|
||||
{
|
||||
}
|
||||
|
||||
void FOculusXRPassthroughModule::StartupModule()
|
||||
{
|
||||
}
|
||||
|
||||
void FOculusXRPassthroughModule::ShutdownModule()
|
||||
{
|
||||
}
|
||||
|
||||
IMPLEMENT_MODULE(FOculusXRPassthroughModule, OculusXRPassthrough)
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
#include "IOculusXRPassthroughModule.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "OculusXRPassthrough"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// FOculusXRPassthroughModule
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
class FOculusXRPassthroughModule : public IOculusXRPassthroughModule
|
||||
{
|
||||
public:
|
||||
FOculusXRPassthroughModule();
|
||||
|
||||
static inline FOculusXRPassthroughModule& Get()
|
||||
{
|
||||
return FModuleManager::LoadModuleChecked<FOculusXRPassthroughModule>("OculusXRPassthrough");
|
||||
}
|
||||
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
};
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
Reference in New Issue
Block a user