Fix RectAtlas regression
This commit is contained in:
@@ -17,8 +17,8 @@ namespace CSG
|
||||
|
||||
struct Node : RectPackNode<float>
|
||||
{
|
||||
Node(float x, float y, float width, float height)
|
||||
: RectPackNode<float>(x, y, width, height)
|
||||
Node(Size x, Size y, Size width, Size height)
|
||||
: RectPackNode(x, y, width, height)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace CSG
|
||||
|
||||
Node* Insert(ChartType chart)
|
||||
{
|
||||
return _root.Insert(chart->Size.X, chart->Size.Y, _chartsPadding, chart, _atlasSize);
|
||||
return _root.Insert(chart->Size.X, chart->Size.Y, chart, _atlasSize);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -72,8 +72,8 @@ void RepackMeshLightmapUVs(ModelData& data)
|
||||
// Build list of meshes with their area
|
||||
struct LightmapUVsPack : RectPackNode<float>
|
||||
{
|
||||
LightmapUVsPack(float x, float y, float width, float height)
|
||||
: RectPackNode<float>(x, y, width, height)
|
||||
LightmapUVsPack(Size x, Size y, Size width, Size height)
|
||||
: RectPackNode(x, y, width, height)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -110,10 +110,10 @@ void RepackMeshLightmapUVs(ModelData& data)
|
||||
bool failed = false;
|
||||
const float chartsPadding = (4.0f / 256.0f) * atlasSize;
|
||||
RectPackAtlas<LightmapUVsPack> atlas;
|
||||
atlas.Init(chartsPadding, chartsPadding);
|
||||
atlas.Init(atlasSize, atlasSize, chartsPadding);
|
||||
for (auto& entry : entries)
|
||||
{
|
||||
entry.Slot = atlas.Insert(entry.Size, entry.Size, chartsPadding);
|
||||
entry.Slot = atlas.Insert(entry.Size, entry.Size);
|
||||
if (entry.Slot == nullptr)
|
||||
{
|
||||
// Failed to insert surface, increase atlas size and try again
|
||||
|
||||
@@ -36,7 +36,8 @@ void FontTextureAtlas::Init(uint32 width, uint32 height)
|
||||
// Setup
|
||||
_width = width;
|
||||
_height = height;
|
||||
_atlas.Init(_width, _height, GetPaddingAmount());
|
||||
const uint32 padding = GetPaddingAmount() * 2; // Double the padding so each slot has own border around it
|
||||
_atlas.Init(_width, _height, padding);
|
||||
_isDirty = false;
|
||||
|
||||
// Reserve upload data memory
|
||||
@@ -44,9 +45,9 @@ void FontTextureAtlas::Init(uint32 width, uint32 height)
|
||||
Platform::MemoryClear(_data.Get(), _data.Capacity());
|
||||
}
|
||||
|
||||
FontTextureAtlasSlot* FontTextureAtlas::AddEntry(uint32 targetWidth, uint32 targetHeight, const Array<byte>& data)
|
||||
FontTextureAtlasSlot* FontTextureAtlas::AddEntry(uint32 width, uint32 height, const Array<byte>& data)
|
||||
{
|
||||
if (targetWidth == 0 || targetHeight == 0)
|
||||
if (width == 0 || height == 0)
|
||||
return nullptr;
|
||||
|
||||
// Try to find slot for the texture
|
||||
@@ -54,7 +55,7 @@ FontTextureAtlasSlot* FontTextureAtlas::AddEntry(uint32 targetWidth, uint32 targ
|
||||
for (int32 i = 0; i < _freeSlots.Count(); i++)
|
||||
{
|
||||
FontTextureAtlasSlot* e = _freeSlots[i];
|
||||
if (e->Width == targetWidth && e->Height == targetHeight)
|
||||
if (e->Width == width && e->Height == height)
|
||||
{
|
||||
slot = e;
|
||||
_freeSlots.RemoveAt(i);
|
||||
@@ -63,7 +64,7 @@ FontTextureAtlasSlot* FontTextureAtlas::AddEntry(uint32 targetWidth, uint32 targ
|
||||
}
|
||||
if (!slot)
|
||||
{
|
||||
slot = _atlas.Insert(targetWidth, targetHeight, GetPaddingAmount());
|
||||
slot = _atlas.Insert(width, height);
|
||||
}
|
||||
|
||||
if (slot)
|
||||
@@ -100,11 +101,10 @@ bool FontTextureAtlas::Invalidate(uint32 x, uint32 y, uint32 width, uint32 heigh
|
||||
void FontTextureAtlas::CopyDataIntoSlot(const FontTextureAtlasSlot* slot, const Array<byte>& data)
|
||||
{
|
||||
RowData rowData;
|
||||
rowData.DstData = &_data[slot->Y * _width * _bytesPerPixel + slot->X * _bytesPerPixel];
|
||||
rowData.DstData = _data.Get() + (slot->Y * _width + slot->X) * _bytesPerPixel;
|
||||
rowData.SrcData = data.Get();
|
||||
rowData.DstTextureWidth = _width;
|
||||
rowData.SrcTextureWidth = slot->Width;
|
||||
rowData.RowWidth = slot->Width;
|
||||
rowData.DstWidth = _width;
|
||||
rowData.SrcWidth = slot->Width;
|
||||
rowData.Padding = GetPaddingAmount();
|
||||
|
||||
// Start with padding
|
||||
@@ -148,20 +148,20 @@ byte* FontTextureAtlas::GetSlotData(const FontTextureAtlasSlot* slot, uint32& wi
|
||||
|
||||
void FontTextureAtlas::copyRow(const RowData& copyRowData) const
|
||||
{
|
||||
const byte* srcData = (const byte*)((intptr)copyRowData.SrcData + (intptr)copyRowData.SrcRow * copyRowData.SrcTextureWidth * _bytesPerPixel);
|
||||
byte* dstData = (byte*)((intptr)copyRowData.DstData + (intptr)copyRowData.DstRow * copyRowData.DstTextureWidth * _bytesPerPixel);
|
||||
Platform::MemoryCopy(dstData, srcData, copyRowData.SrcTextureWidth * _bytesPerPixel);
|
||||
const byte* srcData = (const byte*)((intptr)copyRowData.SrcData + (intptr)copyRowData.SrcRow * copyRowData.SrcWidth * _bytesPerPixel);
|
||||
byte* dstData = (byte*)((intptr)copyRowData.DstData + (intptr)copyRowData.DstRow * copyRowData.DstWidth * _bytesPerPixel);
|
||||
Platform::MemoryCopy(dstData, srcData, copyRowData.SrcWidth * _bytesPerPixel);
|
||||
|
||||
if (copyRowData.Padding > 0)
|
||||
{
|
||||
const uint32 padSize = copyRowData.Padding * _bytesPerPixel;
|
||||
byte* dstPaddingPixelLeft = (byte*)((intptr)copyRowData.DstData + (intptr)copyRowData.DstRow * copyRowData.DstTextureWidth * _bytesPerPixel - padSize);
|
||||
byte* dstPaddingPixelRight = dstPaddingPixelLeft + copyRowData.RowWidth * _bytesPerPixel + padSize;
|
||||
byte* dstPaddingPixelLeft = (byte*)((intptr)copyRowData.DstData + (intptr)copyRowData.DstRow * copyRowData.DstWidth * _bytesPerPixel - padSize);
|
||||
byte* dstPaddingPixelRight = dstPaddingPixelLeft + copyRowData.SrcWidth * _bytesPerPixel + padSize;
|
||||
if (_paddingStyle == DilateBorder)
|
||||
{
|
||||
// Dilate left and right sides of the padded row
|
||||
const byte* firstPixel = srcData;
|
||||
const byte* lastPixel = srcData + (copyRowData.SrcTextureWidth - 1) * _bytesPerPixel;
|
||||
const byte* lastPixel = srcData + (copyRowData.SrcWidth - 1) * _bytesPerPixel;
|
||||
Platform::MemoryCopy(dstPaddingPixelLeft, firstPixel, padSize);
|
||||
Platform::MemoryCopy(dstPaddingPixelRight, lastPixel, padSize);
|
||||
}
|
||||
@@ -176,8 +176,8 @@ void FontTextureAtlas::copyRow(const RowData& copyRowData) const
|
||||
|
||||
void FontTextureAtlas::zeroRow(const RowData& copyRowData) const
|
||||
{
|
||||
byte* dstData = (byte*)((intptr)copyRowData.DstData + (intptr)copyRowData.DstRow * copyRowData.DstTextureWidth * _bytesPerPixel);
|
||||
uint32 dstSize = copyRowData.RowWidth * _bytesPerPixel;
|
||||
byte* dstData = (byte*)((intptr)copyRowData.DstData + (intptr)copyRowData.DstRow * copyRowData.DstWidth * _bytesPerPixel);
|
||||
uint32 dstSize = copyRowData.SrcWidth * _bytesPerPixel;
|
||||
if (copyRowData.Padding > 0)
|
||||
{
|
||||
// Extend clear by left and right borders of the padded row
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
/// </summary>
|
||||
struct FontTextureAtlasSlot : RectPackNode<>
|
||||
{
|
||||
FontTextureAtlasSlot(uint32 x, uint32 y, uint32 width, uint32 height)
|
||||
: RectPackNode<>(x, y, width, height)
|
||||
FontTextureAtlasSlot(Size x, Size y, Size width, Size height)
|
||||
: RectPackNode(x, y, width, height)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -37,9 +37,8 @@ private:
|
||||
uint8* DstData;
|
||||
int32 SrcRow;
|
||||
int32 DstRow;
|
||||
int32 RowWidth;
|
||||
int32 SrcTextureWidth;
|
||||
int32 DstTextureWidth;
|
||||
int32 SrcWidth;
|
||||
int32 DstWidth;
|
||||
uint32 Padding;
|
||||
};
|
||||
|
||||
@@ -154,11 +153,11 @@ public:
|
||||
/// <summary>
|
||||
/// Adds the new entry to the atlas
|
||||
/// </summary>
|
||||
/// <param name="targetWidth">Width of the entry.</param>
|
||||
/// <param name="targetHeight">Height of the entry.</param>
|
||||
/// <param name="width">Width of the entry.</param>
|
||||
/// <param name="height">Height of the entry.</param>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <returns>The atlas slot occupied by the new entry.</returns>
|
||||
FontTextureAtlasSlot* AddEntry(uint32 targetWidth, uint32 targetHeight, const Array<byte>& data);
|
||||
FontTextureAtlasSlot* AddEntry(uint32 width, uint32 height, const Array<byte>& data);
|
||||
|
||||
/// <summary>
|
||||
/// Invalidates the cached dynamic entry from the atlas.
|
||||
|
||||
@@ -73,8 +73,8 @@ struct GlobalSurfaceAtlasTile : RectPackNode<uint16>
|
||||
uint32 Address;
|
||||
uint32 ObjectAddressOffset;
|
||||
|
||||
GlobalSurfaceAtlasTile(uint16 x, uint16 y, uint16 width, uint16 height)
|
||||
: RectPackNode<uint16>(x, y, width, height)
|
||||
GlobalSurfaceAtlasTile(Size x, Size y, Size width, Size height)
|
||||
: RectPackNode(x, y, width, height)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1236,7 +1236,7 @@ void GlobalSurfaceAtlasPass::RasterizeActor(Actor* actor, void* actorObject, con
|
||||
uint16 tilePixels = tileResolution * tileResolution;
|
||||
GlobalSurfaceAtlasTile* tile = nullptr;
|
||||
if (tilePixels <= surfaceAtlasData.AtlasPixelsTotal - surfaceAtlasData.AtlasPixelsUsed)
|
||||
tile = surfaceAtlasData.Atlas.Insert(tileResolution, tileResolution, 0, &surfaceAtlasData, actorObject, tileIndex);
|
||||
tile = surfaceAtlasData.Atlas.Insert(tileResolution, tileResolution, &surfaceAtlasData, actorObject, tileIndex);
|
||||
if (tile)
|
||||
{
|
||||
if (!object)
|
||||
|
||||
@@ -43,8 +43,8 @@ struct ShadowsAtlasRectTile : RectPackNode<uint16>
|
||||
{
|
||||
bool IsStatic;
|
||||
|
||||
ShadowsAtlasRectTile(uint16 x, uint16 y, uint16 width, uint16 height)
|
||||
: RectPackNode<uint16>(x, y, width, height)
|
||||
ShadowsAtlasRectTile(Size x, Size y, Size width, Size height)
|
||||
: RectPackNode(x, y, width, height)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -682,7 +682,7 @@ bool ShadowsPass::SetupLight(ShadowsCustomBuffer& shadows, RenderContext& render
|
||||
auto& tile = atlasLight.Tiles[tileIndex];
|
||||
if (tile.StaticRectTile == nullptr)
|
||||
{
|
||||
tile.StaticRectTile = shadows.StaticAtlas.Insert(atlasLight.StaticResolution, atlasLight.StaticResolution, 0, &shadows, true);
|
||||
tile.StaticRectTile = shadows.StaticAtlas.Insert(atlasLight.StaticResolution, atlasLight.StaticResolution, &shadows, true);
|
||||
if (!tile.StaticRectTile)
|
||||
{
|
||||
// Failed to insert tile to switch back to the default rendering
|
||||
@@ -1225,7 +1225,7 @@ RETRY_ATLAS_SETUP:
|
||||
bool failedToInsert = false;
|
||||
for (int32 tileIndex = 0; tileIndex < atlasLight.TilesNeeded; tileIndex++)
|
||||
{
|
||||
auto rectTile = shadows.Atlas.Insert(atlasLight.Resolution, atlasLight.Resolution, 0, &shadows, false);
|
||||
auto rectTile = shadows.Atlas.Insert(atlasLight.Resolution, atlasLight.Resolution, &shadows, false);
|
||||
if (!rectTile)
|
||||
{
|
||||
// Free any previous tiles that were added
|
||||
|
||||
@@ -17,8 +17,8 @@ namespace ShadowsOfMordor
|
||||
{
|
||||
Builder::LightmapUVsChart* Chart = nullptr;
|
||||
|
||||
Node(int32 x, int32 y, int32 width, int32 height)
|
||||
: RectPackNode<int32>(x, y, width, height)
|
||||
Node(Size x, Size y, Size width, Size height)
|
||||
: RectPackNode(x, y, width, height)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace ShadowsOfMordor
|
||||
/// <returns></returns>
|
||||
Node* Insert(Builder::LightmapUVsChart* chart)
|
||||
{
|
||||
return _root.Insert(chart->Width, chart->Height, _settings->ChartsPadding, chart, _settings);
|
||||
return _root.Insert(chart->Width, chart->Height, chart, _settings);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -31,12 +31,6 @@ struct RectPackNode
|
||||
, Height(height)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator<(const RectPackNode& other) const
|
||||
{
|
||||
// Sort largest to smallest
|
||||
return Width * Height > other.Width * other.Height;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -118,7 +112,7 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="atlasWidth">The atlas width (in pixels).</param>
|
||||
/// <param name="atlasHeight">The atlas height (in pixels).</param>
|
||||
/// <param name="bordersPadding">The atlas borders padding (in pixels).</param>
|
||||
/// <param name="bordersPadding">The nodes padding (in pixels). Distance from node contents to atlas borders or other nodes.</param>
|
||||
void Init(Size atlasWidth, Size atlasHeight, Size bordersPadding = 0)
|
||||
{
|
||||
Width = atlasWidth;
|
||||
@@ -126,7 +120,7 @@ public:
|
||||
BordersPadding = bordersPadding;
|
||||
Nodes.Clear();
|
||||
FreeNodes.Clear();
|
||||
Nodes.Add(NodeType(bordersPadding, bordersPadding, atlasWidth - bordersPadding * 2, atlasHeight - bordersPadding * 2));
|
||||
Nodes.Add(NodeType(bordersPadding, bordersPadding, atlasWidth - bordersPadding, atlasHeight - bordersPadding));
|
||||
FreeNodes.Add(&Nodes[0]);
|
||||
}
|
||||
|
||||
@@ -145,62 +139,61 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="width">The node width (in pixels).</param>
|
||||
/// <param name="height">The node height (in pixels).</param>
|
||||
/// <param name="padding">The node padding margin (in pixels) around its contents.</param>
|
||||
/// <param name="args">The additional arguments.</param>
|
||||
/// <returns>The node that contains inserted an item or null if failed to find a free space.</returns>
|
||||
template<class... Args>
|
||||
NodeType* Insert(Size width, Size height, Size padding, Args&&... args)
|
||||
NodeType* Insert(Size width, Size height, Args&&... args)
|
||||
{
|
||||
NodeType* result = nullptr;
|
||||
const Size paddedWidth = width + padding;
|
||||
const Size paddedHeight = height + padding;
|
||||
const Size paddedWidth = width + BordersPadding;
|
||||
const Size paddedHeight = height + BordersPadding;
|
||||
|
||||
// Search free nodes from back to front and find the one that fits requested item size
|
||||
// TODO: FreeNodes are sorted so use Binary Search to quickly find the first tile that might have enough space for insert
|
||||
for (int32 i = FreeNodes.Count() - 1; i >= 0; i--)
|
||||
{
|
||||
NodeType& freeNode = *FreeNodes.Get()[i];
|
||||
if (paddedWidth > freeNode.Width || paddedHeight > freeNode.Height)
|
||||
NodeType* freeNode = FreeNodes.Get()[i];
|
||||
if (paddedWidth > freeNode->Width || paddedHeight > freeNode->Height)
|
||||
{
|
||||
// Not enough space
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if there will be some remaining space left in this node
|
||||
if (freeNode.Width != paddedWidth || freeNode.Height != paddedHeight)
|
||||
if (freeNode->Width != width || freeNode->Height != height)
|
||||
{
|
||||
// Subdivide this node into up to 2 additional nodes
|
||||
const Size remainingWidth = freeNode.Width - paddedWidth;
|
||||
const Size remainingHeight = freeNode.Height - paddedHeight;
|
||||
const Size remainingWidth = freeNode->Width - paddedWidth;
|
||||
const Size remainingHeight = freeNode->Height - paddedHeight;
|
||||
|
||||
// Split the remaining area around this node into two children
|
||||
SizeRect bigger, smaller;
|
||||
if (remainingHeight <= remainingWidth)
|
||||
{
|
||||
// Split vertically
|
||||
smaller = SizeRect(freeNode.X, freeNode.Y + paddedHeight, width, remainingHeight);
|
||||
bigger = SizeRect(freeNode.X + paddedWidth, freeNode.Y, remainingWidth, freeNode.Height);
|
||||
smaller = SizeRect(freeNode->X, freeNode->Y + paddedHeight, width, remainingHeight);
|
||||
bigger = SizeRect(freeNode->X + paddedWidth, freeNode->Y, remainingWidth, freeNode->Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Split horizontally
|
||||
smaller = SizeRect(freeNode.X + paddedWidth, freeNode.Y, remainingWidth, height);
|
||||
bigger = SizeRect(freeNode.X, freeNode.Y + paddedHeight, freeNode.Width, remainingHeight);
|
||||
smaller = SizeRect(freeNode->X + paddedWidth, freeNode->Y, remainingWidth, height);
|
||||
bigger = SizeRect(freeNode->X, freeNode->Y + paddedHeight, freeNode->Width, remainingHeight);
|
||||
}
|
||||
if (smaller.W * smaller.H > bigger.W * bigger.H)
|
||||
Swap(bigger, smaller);
|
||||
if (bigger.W * bigger.H > padding)
|
||||
if (bigger.W * bigger.H > BordersPadding)
|
||||
AddFreeNode(Nodes.Add(NodeType(bigger.X, bigger.Y, bigger.W, bigger.H)));
|
||||
if (smaller.W * smaller.H > padding)
|
||||
if (smaller.W * smaller.H > BordersPadding)
|
||||
AddFreeNode(Nodes.Add(NodeType(smaller.X, smaller.Y, smaller.W, smaller.H)));
|
||||
|
||||
// Shrink to the actual area
|
||||
freeNode.Width = width;
|
||||
freeNode.Height = height;
|
||||
freeNode->Width = width;
|
||||
freeNode->Height = height;
|
||||
}
|
||||
|
||||
// Insert into this node
|
||||
result = &freeNode;
|
||||
result = freeNode;
|
||||
FreeNodes.RemoveAtKeepOrder(i);
|
||||
result->OnInsert(Forward<Args>(args)...);
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user