234 lines
7.4 KiB
C++
234 lines
7.4 KiB
C++
/******************************************************************************
|
|
* Copyright (C) Ultraleap, Inc. 2011-2021. *
|
|
* *
|
|
* Use subject to the terms of the Apache License 2.0 available at *
|
|
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *
|
|
* between Ultraleap and you, your company or other organization. *
|
|
******************************************************************************/
|
|
|
|
#include "LeapImage.h"
|
|
|
|
#include "LeapAsync.h"
|
|
|
|
#if (ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 4)
|
|
#include "RenderingThread.h"
|
|
#endif
|
|
|
|
FLeapImage::FLeapImage()
|
|
{
|
|
LeftImageTexture = nullptr;
|
|
RightImageTexture = nullptr;
|
|
Reset();
|
|
}
|
|
|
|
bool FLeapImage::HasSameTextureFormat(UTexture2D* TexturePointer, const LEAP_IMAGE& Image)
|
|
{
|
|
if (TexturePointer == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
#if ENGINE_MAJOR_VERSION >= 5
|
|
return (TexturePointer->IsValidLowLevelFast() && TexturePointer->GetPlatformData() &&
|
|
TexturePointer->GetPlatformData()->SizeX == Image.properties.width &&
|
|
TexturePointer->GetPlatformData()->SizeY == Image.properties.height);
|
|
#else
|
|
return (TexturePointer->IsValidLowLevelFast() && TexturePointer->PlatformData &&
|
|
TexturePointer->PlatformData->SizeX == Image.properties.width &&
|
|
TexturePointer->PlatformData->SizeY == Image.properties.height);
|
|
#endif
|
|
}
|
|
|
|
UTexture2D* FLeapImage::CreateTextureIfNeeded(UTexture2D* TexturePointer, const LEAP_IMAGE& Image)
|
|
{
|
|
if (bIsQuitting)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
if (!HasSameTextureFormat(TexturePointer, Image))
|
|
{
|
|
EPixelFormat PixelFormat = PF_G8;
|
|
if (TexturePointer->IsValidLowLevelFast())
|
|
{
|
|
TexturePointer->RemoveFromRoot();
|
|
}
|
|
TexturePointer = UTexture2D::CreateTransient(Image.properties.width, Image.properties.height, PixelFormat);
|
|
TexturePointer->CompressionSettings = TextureCompressionSettings::TC_Grayscale;
|
|
TexturePointer->UpdateResource();
|
|
TexturePointer->AddToRoot();
|
|
UpdateTextureRegion = FUpdateTextureRegion2D(0, 0, 0, 0, Image.properties.width, Image.properties.height);
|
|
return TexturePointer;
|
|
}
|
|
return TexturePointer;
|
|
}
|
|
|
|
void FLeapImage::UpdateTextureRegions(UTexture2D* Texture, int32 MipIndex, uint32 NumRegions, FUpdateTextureRegion2D* Regions,
|
|
uint32 SrcPitch, uint32 SrcBpp, uint8* SrcData, bool bFreeData)
|
|
{
|
|
#if ENGINE_MAJOR_VERSION >= 5
|
|
if (Texture->GetResource())
|
|
#else
|
|
if (Texture->Resource)
|
|
#endif
|
|
{
|
|
struct FUpdateTextureRegionsData
|
|
{
|
|
FTexture2DResource* Texture2DResource;
|
|
int32 MipIndex;
|
|
uint32 NumRegions;
|
|
FUpdateTextureRegion2D* Regions;
|
|
uint32 SrcPitch;
|
|
uint32 SrcBpp;
|
|
uint8* SrcData;
|
|
bool bFreeData;
|
|
FLeapImage* LeapImagePtr;
|
|
};
|
|
|
|
FUpdateTextureRegionsData* RegionData = new FUpdateTextureRegionsData;
|
|
#if ENGINE_MAJOR_VERSION >= 5
|
|
RegionData->Texture2DResource = (FTexture2DResource*) Texture->GetResource();
|
|
#else
|
|
RegionData->Texture2DResource = (FTexture2DResource*) Texture->Resource;
|
|
#endif
|
|
RegionData->MipIndex = MipIndex;
|
|
RegionData->NumRegions = NumRegions;
|
|
RegionData->Regions = Regions;
|
|
RegionData->SrcPitch = SrcPitch;
|
|
RegionData->SrcBpp = SrcBpp;
|
|
RegionData->SrcData = SrcData;
|
|
RegionData->bFreeData = bFreeData;
|
|
RegionData->LeapImagePtr = this;
|
|
|
|
ENQUEUE_RENDER_COMMAND(UpdateTextureRegionsData)
|
|
([RegionData](FRHICommandListImmediate& RHICmdList) {
|
|
for (uint32 RegionIndex = 0; RegionIndex < RegionData->NumRegions; ++RegionIndex)
|
|
{
|
|
int32 CurrentFirstMip = RegionData->Texture2DResource->GetCurrentFirstMip();
|
|
if (RegionData->MipIndex >= CurrentFirstMip)
|
|
{
|
|
RHIUpdateTexture2D(RegionData->Texture2DResource->GetTexture2DRHI(), RegionData->MipIndex - CurrentFirstMip,
|
|
RegionData->Regions[RegionIndex], RegionData->SrcPitch,
|
|
RegionData->SrcData + RegionData->Regions[RegionIndex].SrcY * RegionData->SrcPitch +
|
|
RegionData->Regions[RegionIndex].SrcX * RegionData->SrcBpp);
|
|
}
|
|
}
|
|
if (RegionData->bFreeData)
|
|
{
|
|
FMemory::Free(RegionData->SrcData);
|
|
}
|
|
RegionData->LeapImagePtr->bRenderDidUpdate = true;
|
|
delete RegionData;
|
|
}); // End Enqueue
|
|
}
|
|
}
|
|
|
|
void FLeapImage::UpdateTextureRegions(UTexture2D* Texture, const LEAP_IMAGE& Image, uint8* SrcData)
|
|
{
|
|
UpdateTextureRegions(Texture, 0, 1, &UpdateTextureRegion, Image.properties.width, Image.properties.bpp, SrcData, true);
|
|
}
|
|
|
|
void FLeapImage::UpdateTextureOnGameThread(UTexture2D* Texture, uint8* SrcData, const int32 BufferLength)
|
|
{
|
|
#if ENGINE_MAJOR_VERSION >= 5
|
|
uint8* MipData = static_cast<uint8*>(Texture->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_WRITE));
|
|
#else
|
|
uint8* MipData = static_cast<uint8*>(Texture->PlatformData->Mips[0].BulkData.Lock(LOCK_READ_WRITE));
|
|
#endif
|
|
memcpy(MipData, SrcData, BufferLength);
|
|
|
|
#if ENGINE_MAJOR_VERSION >= 5
|
|
Texture->GetPlatformData()->Mips[0].BulkData.Unlock();
|
|
#else
|
|
Texture->PlatformData->Mips[0].BulkData.Unlock();
|
|
#endif
|
|
Texture->UpdateResource();
|
|
}
|
|
|
|
void FLeapImage::OnImage(const LEAP_IMAGE_EVENT* ImageEvent)
|
|
{
|
|
// Don't schedule more events if we've received quitting signal or we haven't updated the last render
|
|
if (bIsQuitting || !bRenderDidUpdate)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const LEAP_IMAGE& LeftLeapImage = ImageEvent->image[0];
|
|
const LEAP_IMAGE& RightLeapImage = ImageEvent->image[1];
|
|
const int32 BufferSize =
|
|
LeftLeapImage.properties.height * LeftLeapImage.properties.width * LeftLeapImage.properties.bpp; // same size for both
|
|
if (LeftImageBuffer.Num() != BufferSize)
|
|
{
|
|
LeftImageBuffer.SetNumUninitialized(BufferSize);
|
|
}
|
|
if (RightImageBuffer.Num() != BufferSize)
|
|
{
|
|
RightImageBuffer.SetNumUninitialized(BufferSize);
|
|
}
|
|
|
|
// Texture allocation
|
|
LeftImageTexture = CreateTextureIfNeeded(LeftImageTexture, LeftLeapImage);
|
|
if (LeftImageTexture)
|
|
{
|
|
// memcopy to a safe resource area
|
|
uint8* SrcPtr = (uint8*) LeftLeapImage.data + LeftLeapImage.offset;
|
|
FMemory::Memcpy(LeftImageBuffer.GetData(), SrcPtr, BufferSize);
|
|
}
|
|
|
|
RightImageTexture = CreateTextureIfNeeded(RightImageTexture, RightLeapImage);
|
|
if (RightImageTexture)
|
|
{
|
|
// memcopy to a safe resource area
|
|
uint8* SrcPtr = (uint8*) RightLeapImage.data + RightLeapImage.offset;
|
|
FMemory::Memcpy(RightImageBuffer.GetData(), SrcPtr, BufferSize);
|
|
}
|
|
|
|
bRenderDidUpdate = false;
|
|
|
|
if (OnImageCallback.IsBound())
|
|
{
|
|
if (LeftImageTexture && RightImageTexture)
|
|
{
|
|
FLeapAsync::RunShortLambdaOnGameThread([&, BufferSize] {
|
|
if (bIsQuitting)
|
|
{
|
|
return;
|
|
}
|
|
// This is sufficient for now since leap images are small
|
|
UpdateTextureOnGameThread(LeftImageTexture, LeftImageBuffer.GetData(), BufferSize);
|
|
UpdateTextureOnGameThread(RightImageTexture, RightImageBuffer.GetData(), BufferSize);
|
|
bRenderDidUpdate = true;
|
|
|
|
// Todo: swap to optimized when ready
|
|
// UpdateTextureRegions(LeftImageTexture, LeftLeapImage, LeftImageBuffer.GetData());
|
|
// UpdateTextureRegions(RightImageTexture, RightLeapImage, RightImageBuffer.GetData());
|
|
|
|
OnImageCallback.Broadcast(LeftImageTexture, RightImageTexture);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
void FLeapImage::CleanupImageData()
|
|
{
|
|
if (LeftImageTexture != nullptr && LeftImageTexture->IsValidLowLevelFast())
|
|
{
|
|
LeftImageTexture->RemoveFromRoot();
|
|
RightImageTexture = nullptr;
|
|
}
|
|
if (RightImageTexture != nullptr && RightImageTexture->IsValidLowLevelFast())
|
|
{
|
|
RightImageTexture->RemoveFromRoot();
|
|
RightImageTexture = nullptr;
|
|
}
|
|
LeftImageBuffer.Empty();
|
|
RightImageBuffer.Empty();
|
|
bIsQuitting = true;
|
|
}
|
|
|
|
void FLeapImage::Reset()
|
|
{
|
|
CleanupImageData();
|
|
bIsQuitting = false;
|
|
bRenderDidUpdate = true;
|
|
}
|