Files
o3de/Gems/SVOGI/Code/Source/TextureBlockPacker.cpp
T

273 lines
8.9 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : 3D block atlas
#include "SVOGI_precompiled.h"
#include "TextureBlockPacker.h"
namespace SVOGI
{
const AZ::u32 nHS = 4;
const AZ::u8 TextureBlock3D::s_freeBlock = 0xff;
const AZ::s32 TextureBlockPacker3D::s_invalidBlockID = -1;
TextureBlockPacker3D::TextureBlockPacker3D(const AZ::u32 dwLogWidth, const AZ::u32 dwLogHeight, const AZ::u32 dwLogDepth, bool bNonPow)
{
if (bNonPow)
{
m_width = dwLogWidth;
m_height = dwLogHeight;
m_depth = dwLogDepth;
}
else
{
m_width = 1 << dwLogWidth;
m_height = 1 << dwLogHeight;
m_depth = 1 << dwLogDepth;
}
m_blockBitmap.resize(m_width * m_height * m_depth, s_invalidBlockID);
m_blockUsageGrid.resize(m_width * m_height * m_depth / nHS / nHS / nHS, 0);
m_blocks.reserve(m_width * m_height * m_depth);
}
TextureBlock3D* TextureBlockPacker3D::GetBlockInfo(const AZ::s32 blockID)
{
if (blockID < 0 || blockID >= static_cast<AZ::s32>(m_blocks.size()))
{
return nullptr;
}
TextureBlock3D* ref = &m_blocks[blockID];
if (ref->IsFree())
{
return nullptr;
}
return ref;
}
void TextureBlockPacker3D::RemoveBlock(const AZ::s32 blockID)
{
TextureBlock3D* blockInfo = GetBlockInfo(blockID);
if (blockInfo)
{
FreeBlock(*blockInfo);
}
}
void TextureBlockPacker3D::FreeBlock(TextureBlock3D& blockInfo)
{
if (!blockInfo.IsFree())
{
FillRect(blockInfo, s_invalidBlockID);
blockInfo.MarkFree();
}
}
AZ::s32 TextureBlockPacker3D::AddBlock(const AZ::u32 dwLogWidth, const AZ::u32 dwLogHeight, const AZ::u32 dwLogDepth, const AZ::Aabb &worldBox)
{
if (!dwLogWidth || !dwLogHeight || !dwLogDepth)
{
assert(!"Empty block");
}
AZ::u32 dwLocalWidth = dwLogWidth;
AZ::u32 dwLocalHeight = dwLogHeight;
AZ::u32 dwLocalDepth = dwLogDepth;
AZ::u32 nCountNeeded = dwLocalWidth * dwLocalHeight * dwLocalDepth;
AZ::u32 dwW = m_width / nHS;
AZ::u32 dwH = m_height / nHS;
AZ::u32 dwD = m_depth / nHS;
for (AZ::u32 nZ = 0; nZ < dwD; nZ++)
{
for (AZ::u32 nY = 0; nY < dwH; nY++)
{
for (AZ::u32 nX = 0; nX < dwW; nX++)
{
AZ::u32 dwMinX = nX * nHS;
AZ::u32 dwMinY = nY * nHS;
AZ::u32 dwMinZ = nZ * nHS;
AZ::u32 dwMaxX = (nX + 1) * nHS;
AZ::u32 dwMaxY = (nY + 1) * nHS;
AZ::u32 dwMaxZ = (nZ + 1) * nHS;
AZ::u32 nCountFree = nHS * nHS * nHS - m_blockUsageGrid[nX + nY * dwW + nZ * dwW * dwH];
if (nCountNeeded <= nCountFree)
{
TextureBlock3D testblock;
for (AZ::u32 dwZ = dwMinZ; dwZ < dwMaxZ; dwZ += dwLocalDepth)
{
for (AZ::u32 dwY = dwMinY; dwY < dwMaxY; dwY += dwLocalHeight)
{
for (AZ::u32 dwX = dwMinX; dwX < dwMaxX; dwX += dwLocalWidth)
{
testblock.m_minX = dwX;
testblock.m_maxX = dwX + dwLocalWidth;
testblock.m_minY = dwY;
testblock.m_maxY = dwY + dwLocalHeight;
testblock.m_minZ = dwZ;
testblock.m_maxZ = dwZ + dwLocalDepth;
testblock.m_worldBox = worldBox;
testblock.m_textureBox.SetMin(AZ::Vector3((float)testblock.m_minX / (float)m_width, (float)testblock.m_minY / (float)m_height, (float)testblock.m_minZ / (float)m_depth));
testblock.m_textureBox.SetMax(AZ::Vector3((float)testblock.m_maxX / (float)m_width, (float)testblock.m_maxY / (float)m_height, (float)testblock.m_maxZ / (float)m_depth));
testblock.m_atlasOffset = testblock.m_minZ * m_width * m_height + testblock.m_minY * m_width + testblock.m_minX;
if (IsFree(testblock))
{
AZ::s32 blockID = FindFreeBlockIDOrCreateNew();
m_blocks[blockID] = testblock;
FillRect(testblock, blockID);
return blockID;
}
}
}
}
}
}
}
}
return s_invalidBlockID; // no space left to this block
}
void TextureBlockPacker3D::UpdateUsageGrid(const TextureBlock3D& rectIn)
{
TextureBlock3D rectUM;
rectUM.m_minX = rectIn.m_minX / nHS;
rectUM.m_minY = rectIn.m_minY / nHS;
rectUM.m_minZ = rectIn.m_minZ / nHS;
rectUM.m_maxX = (rectIn.m_maxX - 1) / nHS + 1;
rectUM.m_maxY = (rectIn.m_maxY - 1) / nHS + 1;
rectUM.m_maxZ = (rectIn.m_maxZ - 1) / nHS + 1;
AZ::u32 dwW = m_width / nHS;
AZ::u32 dwH = m_height / nHS;
for (AZ::u32 dwZ = rectUM.m_minZ; dwZ < rectUM.m_maxZ; ++dwZ)
{
for (AZ::u32 dwY = rectUM.m_minY; dwY < rectUM.m_maxY; ++dwY)
{
for (AZ::u32 dwX = rectUM.m_minX; dwX < rectUM.m_maxX; ++dwX)
{
TextureBlock3D rectTest = rectUM;
rectTest.m_minX = dwX * nHS;
rectTest.m_minY = dwY * nHS;
rectTest.m_minZ = dwZ * nHS;
rectTest.m_maxX = (dwX + 1) * nHS;
rectTest.m_maxY = (dwY + 1) * nHS;
rectTest.m_maxZ = (dwZ + 1) * nHS;
m_blockUsageGrid[dwX + dwY * dwW + dwZ * dwW * dwH] = GetUsedSlotsCount(rectTest);
}
}
}
}
void TextureBlockPacker3D::FillRect(const TextureBlock3D& rect, AZ::s32 value)
{
for (AZ::u32 dwZ = rect.m_minZ; dwZ < rect.m_maxZ; ++dwZ)
{
for (AZ::u32 dwY = rect.m_minY; dwY < rect.m_maxY; ++dwY)
{
for (AZ::u32 dwX = rect.m_minX; dwX < rect.m_maxX; ++dwX)
{
m_blockBitmap[dwX + dwY * m_width + dwZ * m_width * m_height] = value;
}
}
}
UpdateUsageGrid(rect);
}
AZ::u32 TextureBlockPacker3D::GetUsedSlotsCount(const TextureBlock3D& rect) const
{
AZ::u32 nCount = 0;
for (AZ::u32 dwZ = rect.m_minZ; dwZ < rect.m_maxZ; ++dwZ)
{
for (AZ::u32 dwY = rect.m_minY; dwY < rect.m_maxY; ++dwY)
{
for (AZ::u32 dwX = rect.m_minX; dwX < rect.m_maxX; ++dwX)
{
if (m_blockBitmap[dwX + dwY * m_width + dwZ * m_width * m_height] != s_invalidBlockID)
{
nCount++;
}
}
}
}
return nCount;
}
bool TextureBlockPacker3D::IsFree(const TextureBlock3D& rect) const
{
for (AZ::u32 dwZ = rect.m_minZ; dwZ < rect.m_maxZ; ++dwZ)
{
for (AZ::u32 dwY = rect.m_minY; dwY < rect.m_maxY; ++dwY)
{
for (AZ::u32 dwX = rect.m_minX; dwX < rect.m_maxX; ++dwX)
{
if (m_blockBitmap[dwX + dwY * m_width + dwZ * m_width * m_height] != s_invalidBlockID)
{
return false;
}
}
}
}
return true;
}
AZ::s32 TextureBlockPacker3D::FindFreeBlockIDOrCreateNew()
{
AZ::s32 index = 0;
for (; index < m_blocks.size(); ++index)
{
if (m_blocks[index].IsFree())
{
return index; // Found free block
}
}
// Create new block
m_blocks.push_back(TextureBlock3D());
return index;
}
}