You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
534 lines
11 KiB
C++
534 lines
11 KiB
C++
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "Allocator2D.h"
|
|
|
|
namespace MeshDescriptionOp
|
|
{
|
|
FAllocator2D::FAllocator2D(uint32 InWidth, uint32 InHeight)
|
|
: Width(InWidth)
|
|
, Height(InHeight)
|
|
, LastRowFail(-1)
|
|
{
|
|
Pitch = (Width + 63) / 64;
|
|
// alloc +1 to avoid buffer overrun
|
|
Bits = (uint64*)FMemory::Malloc((Pitch * Height + 1) * sizeof(uint64));
|
|
|
|
Rows.Reserve(Height);
|
|
|
|
for (uint32 Y = 0; Y < Height; ++Y)
|
|
{
|
|
FRow Row;
|
|
Row.Index = Y;
|
|
|
|
Rows.Add(Row);
|
|
}
|
|
|
|
Clear();
|
|
}
|
|
|
|
FAllocator2D::~FAllocator2D()
|
|
{
|
|
FMemory::Free(Bits);
|
|
}
|
|
|
|
FAllocator2D::FAllocator2D(const FAllocator2D& Other)
|
|
: Width(Other.Width)
|
|
, Height(Other.Height)
|
|
, Pitch(Other.Pitch)
|
|
, Rows(Other.Rows)
|
|
, LastRowFail(-1)
|
|
{
|
|
Bits = (uint64*)FMemory::Malloc((Pitch * Height + 1) * sizeof(uint64));
|
|
FMemory::Memcpy(Bits, Other.Bits, (Pitch * Height + 1) * sizeof(uint64));
|
|
}
|
|
|
|
FAllocator2D& FAllocator2D::operator=(const FAllocator2D& Other)
|
|
{
|
|
if (Width != Other.Width || Height != Other.Height || Pitch != Other.Pitch)
|
|
{
|
|
Width = Other.Width;
|
|
Height = Other.Height;
|
|
Pitch = Other.Pitch;
|
|
|
|
FMemory::Free(Bits);
|
|
Bits = (uint64*)FMemory::Malloc((Pitch * Height + 1) * sizeof(uint64));
|
|
}
|
|
|
|
FMemory::Memcpy(Bits, Other.Bits, (Pitch * Height + 1) * sizeof(uint64));
|
|
|
|
Rows = Other.Rows;
|
|
|
|
return *this;
|
|
}
|
|
|
|
void FAllocator2D::Clear()
|
|
{
|
|
InitSegments();
|
|
|
|
FMemory::Memzero(Bits, (Pitch * Height + 1) * sizeof(uint64));
|
|
}
|
|
|
|
bool FAllocator2D::Find(FRect& Rect)
|
|
{
|
|
FRect TestRect = Rect;
|
|
for (TestRect.X = 0; TestRect.X <= Width - TestRect.W; TestRect.X++)
|
|
{
|
|
for (TestRect.Y = 0; TestRect.Y <= Height - TestRect.H; TestRect.Y++)
|
|
{
|
|
if (Test(TestRect))
|
|
{
|
|
Rect = TestRect;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool FAllocator2D::FindBitByBit(FRect& Rect, const FAllocator2D& Other)
|
|
{
|
|
FRect TestRect = Rect;
|
|
for (TestRect.X = 0; TestRect.X <= Width - TestRect.W; TestRect.X++)
|
|
{
|
|
for (TestRect.Y = 0; TestRect.Y <= Height - TestRect.H; TestRect.Y++)
|
|
{
|
|
if (Test(TestRect, Other))
|
|
{
|
|
Rect = TestRect;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool FAllocator2D::FindWithSegments(FRect& Rect, FRect BestRect, const FAllocator2D& Other)
|
|
{
|
|
LastRowFail = -1;
|
|
FRect TestRect = Rect;
|
|
|
|
for (TestRect.Y = 0; TestRect.Y <= Height - TestRect.H; ++TestRect.Y)
|
|
{
|
|
uint32 FailedLength = 1;
|
|
|
|
for (TestRect.X = 0; TestRect.X <= Width - TestRect.W; TestRect.X += FailedLength)
|
|
{
|
|
if (TestRect.X + TestRect.Y * Height >= BestRect.X + BestRect.Y * Height)
|
|
{
|
|
// We've moved past the BestRect
|
|
return false;
|
|
}
|
|
|
|
if (TestAllRows(TestRect, Other, FailedLength))
|
|
{
|
|
Rect = TestRect;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void FAllocator2D::Alloc(FRect Rect)
|
|
{
|
|
for (uint32 y = Rect.Y; y < Rect.Y + Rect.H; y++)
|
|
{
|
|
for (uint32 x = Rect.X; x < Rect.X + Rect.W; x++)
|
|
{
|
|
SetBit(x, y);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FAllocator2D::Alloc(FRect Rect, const FAllocator2D& Other)
|
|
{
|
|
for (uint32 y = 0; y < Rect.H; y++)
|
|
{
|
|
for (uint32 x = 0; x < Rect.W; x++)
|
|
{
|
|
if (Other.GetBit(x, y))
|
|
{
|
|
SetBit(x + Rect.X, y + Rect.Y);
|
|
}
|
|
}
|
|
}
|
|
|
|
MergeSegments(Rect, Other);
|
|
}
|
|
|
|
bool FAllocator2D::TestAllRows(FRect Rect, const FAllocator2D& Other, uint32& FailedLength)
|
|
{
|
|
FailedLength = 0.f;
|
|
bool bSuccess = true;
|
|
|
|
if (LastRowFail > 0)
|
|
{
|
|
const FRow& ThisRow = Rows[Rect.Y + LastRowFail];
|
|
const FRow& OtherRow = Other.Rows[LastRowFail];
|
|
|
|
if (!TestRow(ThisRow, OtherRow, Rect, FailedLength))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
LastRowFail = -1;
|
|
|
|
for (uint32 y = 0; y < Rect.H; ++y)
|
|
{
|
|
const FRow& ThisRow = Rows[Rect.Y + y];
|
|
const FRow& OtherRow = Other.Rows[y];
|
|
|
|
uint32 CurrentFailedLength = 0;
|
|
bool bThisRowSuccess = TestRow(ThisRow, OtherRow, Rect, CurrentFailedLength);
|
|
bSuccess = bThisRowSuccess && bSuccess;
|
|
|
|
if (!bThisRowSuccess && CurrentFailedLength > FailedLength)
|
|
{
|
|
LastRowFail = y;
|
|
FailedLength = CurrentFailedLength;
|
|
}
|
|
|
|
if (FailedLength >= Width)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
bool FAllocator2D::TestRow(const FRow& ThisRow, const FRow& OtherRow, FRect Rect, uint32& FailedLength)
|
|
{
|
|
if (ThisRow.LongestSegment < OtherRow.LongestSegment)
|
|
{
|
|
FailedLength = Width;
|
|
return false;
|
|
}
|
|
|
|
uint32 StartFreeSegmentIndex = 0;
|
|
|
|
for (const FSegment& OtherUsedSegment : OtherRow.UsedSegments)
|
|
{
|
|
if (OtherUsedSegment.StartPos >= Rect.W)
|
|
{
|
|
break;
|
|
}
|
|
|
|
bool bFoundSpaceForSegment = false;
|
|
bool bFoundFutureSpaceForSegment = false;
|
|
|
|
const uint32 StartPos = Rect.X + OtherUsedSegment.StartPos;
|
|
const uint32 EndPos = Rect.X + FMath::Min(OtherUsedSegment.StartPos + OtherUsedSegment.Length, Rect.W);
|
|
|
|
for (int32 i = StartFreeSegmentIndex; i < ThisRow.FreeSegments.Num(); ++i)
|
|
{
|
|
const FSegment& ThisFreeSegment = ThisRow.FreeSegments[i];
|
|
|
|
if (StartPos >= ThisFreeSegment.StartPos &&
|
|
EndPos <= ThisFreeSegment.StartPos + ThisFreeSegment.Length)
|
|
{
|
|
StartFreeSegmentIndex = i;
|
|
bFoundSpaceForSegment = true;
|
|
break;
|
|
}
|
|
else if (!bFoundFutureSpaceForSegment &&
|
|
StartPos < ThisFreeSegment.StartPos &&
|
|
OtherUsedSegment.Length <= ThisFreeSegment.Length)
|
|
{
|
|
bFoundFutureSpaceForSegment = true;
|
|
FailedLength = ThisFreeSegment.StartPos - StartPos;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bFoundSpaceForSegment)
|
|
{
|
|
if (!bFoundFutureSpaceForSegment)
|
|
{
|
|
FailedLength = Width;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void FAllocator2D::FlipX(FRect Rect, FMeshDescriptionOperations::ELightmapUVVersion LayoutVersion)
|
|
{
|
|
// If we have empty padding around the Rect, keep it there
|
|
uint32 MaxY = Rect.H - 1;
|
|
for (int32 Y = Rect.H - 1; Y > 0; --Y)
|
|
{
|
|
if (Rows[Y].UsedSegments.Num() > 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
--MaxY;
|
|
}
|
|
|
|
uint32 MaxX = 0;
|
|
if ( LayoutVersion >= FMeshDescriptionOperations::ELightmapUVVersion::Allocator2DFlipFix )
|
|
{
|
|
for ( uint32 Y = 0; Y <= MaxY; ++Y )
|
|
{
|
|
for ( const FSegment& UsedSegment : Rows[ Y ].UsedSegments )
|
|
{
|
|
MaxX = FMath::Max( MaxX, UsedSegment.StartPos + UsedSegment.Length - 1 );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MaxX = Rect.W - 1;
|
|
}
|
|
|
|
for (uint32 Y = 0; Y <= MaxY; ++Y)
|
|
{
|
|
for (uint32 LowX = 0; LowX < (MaxX + 1) / 2; ++LowX)
|
|
{
|
|
uint32 HighX = MaxX - LowX;
|
|
|
|
const uint64 BitLow = GetBit(LowX, Y);
|
|
const uint64 BitHigh = GetBit(HighX, Y);
|
|
|
|
if (BitLow != 0ull)
|
|
{
|
|
SetBit(HighX, Y);
|
|
}
|
|
else
|
|
{
|
|
ClearBit(HighX, Y);
|
|
}
|
|
|
|
if (BitHigh != 0ull)
|
|
{
|
|
SetBit(LowX, Y);
|
|
}
|
|
else
|
|
{
|
|
ClearBit(LowX, Y);
|
|
}
|
|
}
|
|
}
|
|
|
|
CreateUsedSegments();
|
|
}
|
|
|
|
void FAllocator2D::FlipY(FRect Rect)
|
|
{
|
|
uint32 MinY = 0;
|
|
|
|
uint32 MaxY = Rect.H - 1;
|
|
for (int32 Y = Rect.H - 1; Y > 0; --Y)
|
|
{
|
|
if (Rows[Y].UsedSegments.Num() > 0)
|
|
break;
|
|
|
|
--MaxY;
|
|
}
|
|
|
|
for (uint32 LowY = 0; LowY < (MaxY + 1) / 2; ++LowY)
|
|
{
|
|
for (uint32 X = 0; X < Rect.W; ++X)
|
|
{
|
|
uint32 HighY = MaxY - LowY;
|
|
|
|
const uint64 BitLow = GetBit(X, LowY);
|
|
const uint64 BitHigh = GetBit(X, HighY);
|
|
|
|
if (BitLow != 0ull)
|
|
{
|
|
SetBit(X, HighY);
|
|
}
|
|
else
|
|
{
|
|
ClearBit(X, HighY);
|
|
}
|
|
|
|
if (BitHigh != 0ull)
|
|
{
|
|
SetBit(X, LowY);
|
|
}
|
|
else
|
|
{
|
|
ClearBit(X, LowY);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (uint32 LowY = MinY; LowY < (MaxY + 1) / 2; ++LowY)
|
|
{
|
|
const int32 HighY = MaxY - LowY;
|
|
|
|
Rows.Swap(LowY, HighY);
|
|
Rows[LowY].Index = LowY;
|
|
Rows[HighY].Index = HighY;
|
|
}
|
|
}
|
|
|
|
void FAllocator2D::InitSegments()
|
|
{
|
|
FSegment FreeSegment;
|
|
FreeSegment.StartPos = 0;
|
|
FreeSegment.Length = Width;
|
|
|
|
for (FRow& Row : Rows)
|
|
{
|
|
Row.FreeSegments.Empty(1);
|
|
Row.FreeSegments.Add(FreeSegment);
|
|
Row.LongestSegment = FreeSegment.Length;
|
|
|
|
Row.UsedSegments.Empty();
|
|
|
|
}
|
|
}
|
|
|
|
void FAllocator2D::CreateUsedSegments()
|
|
{
|
|
for (uint32 y = 0; y < Height; ++y)
|
|
{
|
|
FRow& CurrentRow = Rows[y];
|
|
CurrentRow.LongestSegment = 0;
|
|
CurrentRow.UsedSegments.Empty();
|
|
|
|
int32 FirstUsedX = -1;
|
|
for (uint32 k = 0; k < Pitch; ++k)
|
|
{
|
|
uint32 x = k * 64;
|
|
|
|
// If all bits are set
|
|
if (Bits[k + y * Pitch] == ~0ull)
|
|
{
|
|
if (FirstUsedX < 0)
|
|
{
|
|
FirstUsedX = x;
|
|
}
|
|
|
|
if (k == Pitch - 1)
|
|
{
|
|
AddUsedSegment(CurrentRow, FirstUsedX, x + 64 - FirstUsedX);
|
|
FirstUsedX = -1;
|
|
}
|
|
}
|
|
// No bits are set
|
|
else if (Bits[k + y * Pitch] == 0ull)
|
|
{
|
|
if (FirstUsedX >= 0)
|
|
{
|
|
AddUsedSegment(CurrentRow, FirstUsedX, x - FirstUsedX);
|
|
FirstUsedX = -1;
|
|
}
|
|
}
|
|
// Some bits are set
|
|
else
|
|
{
|
|
for (uint32 i = 0; i < 64; ++i)
|
|
{
|
|
const uint32 SubX = x + i;
|
|
|
|
if (GetBit(SubX, y))
|
|
{
|
|
if (FirstUsedX < 0)
|
|
{
|
|
FirstUsedX = SubX;
|
|
}
|
|
|
|
if (SubX == Width - 1)
|
|
{
|
|
AddUsedSegment(CurrentRow, FirstUsedX, SubX + 1 - FirstUsedX);
|
|
FirstUsedX = -1;
|
|
}
|
|
}
|
|
else if (FirstUsedX >= 0)
|
|
{
|
|
AddUsedSegment(CurrentRow, FirstUsedX, SubX - FirstUsedX);
|
|
FirstUsedX = -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FAllocator2D::AddUsedSegment(FRow& Row, uint32 StartPos, uint32 Length)
|
|
{
|
|
FSegment UsedSegment;
|
|
UsedSegment.StartPos = StartPos;
|
|
UsedSegment.Length = Length;
|
|
|
|
Row.UsedSegments.Add(UsedSegment);
|
|
|
|
if (UsedSegment.Length > Row.LongestSegment)
|
|
{
|
|
Row.LongestSegment = UsedSegment.Length;
|
|
}
|
|
}
|
|
|
|
void FAllocator2D::MergeSegments(FRect Rect, const FAllocator2D& Other)
|
|
{
|
|
for (uint32 y = 0; y < Rect.H; ++y)
|
|
{
|
|
FRow& ThisRow = Rows[Rect.Y + y];
|
|
const FRow& OtherRow = Other.Rows[y];
|
|
|
|
for (const FSegment& OtherUsedSegment : OtherRow.UsedSegments)
|
|
{
|
|
for (int32 i = 0; i < ThisRow.FreeSegments.Num(); ++i)
|
|
{
|
|
FSegment& ThisFreeSegment = ThisRow.FreeSegments[i];
|
|
|
|
uint32 StartPos = Rect.X + OtherUsedSegment.StartPos;
|
|
|
|
if (StartPos >= ThisFreeSegment.StartPos &&
|
|
StartPos < ThisFreeSegment.StartPos + ThisFreeSegment.Length)
|
|
{
|
|
if (ThisFreeSegment.Length == 1)
|
|
{
|
|
ThisRow.FreeSegments.RemoveAtSwap(i);
|
|
break;
|
|
}
|
|
|
|
FSegment FirstSegment;
|
|
FirstSegment.StartPos = ThisFreeSegment.StartPos;
|
|
FirstSegment.Length = StartPos - ThisFreeSegment.StartPos;
|
|
|
|
uint32 EndPos = Rect.X + FMath::Min(OtherUsedSegment.StartPos + OtherUsedSegment.Length, Rect.W) - 1;
|
|
|
|
FSegment SecondSegment;
|
|
SecondSegment.StartPos = EndPos + 1;
|
|
SecondSegment.Length = ThisFreeSegment.StartPos + ThisFreeSegment.Length - SecondSegment.StartPos;
|
|
|
|
ThisRow.FreeSegments.RemoveAtSwap(i);
|
|
|
|
if (FirstSegment.Length > 0)
|
|
{
|
|
ThisRow.FreeSegments.Add(FirstSegment);
|
|
}
|
|
|
|
if (SecondSegment.Length > 0)
|
|
{
|
|
ThisRow.FreeSegments.Add(SecondSegment);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
ThisRow.FreeSegments.Sort();
|
|
|
|
ThisRow.LongestSegment = 0;
|
|
for (const FSegment& Segment : ThisRow.FreeSegments)
|
|
{
|
|
ThisRow.LongestSegment = FMath::Max(Segment.Length, ThisRow.LongestSegment);
|
|
}
|
|
}
|
|
}
|
|
} |