[英]Why a texture cannot find its position in a texture atlas after the atlas has been resized?
I've got texture atlas from: https://straypixels.net/texture-packing-for-fonts/我的纹理图集来自: https : //straypixels.net/texture-packing-for-fonts/
struct TextureNode
{
TextureNode(const Vector2<uint32>& origin, const Vector2<uint32>& size)
: origin(origin), size(size)
{
}
Vector2<uint32> origin = 0; // Top left of the rectangle this node represents
Vector2<uint32> size = 0; // Size of the rectangle this node represents
bool empty = true; // true if this node is a leaf and is filled
std::unique_ptr<TextureNode> left = nullptr; // Left (or top) subdivision
std::unique_ptr<TextureNode> right = nullptr; // Right (or bottom) subdivision
};
struct TextureAtlas
{
TextureAtlas() = default;
~TextureAtlas()
{
buffer.clear();
}
void Create(const Vector2<uint32>& size)
{
textureSize = size;
rootNode = std::make_unique<::TextureNode>(0, size);
buffer.resize(size.w * size.h);
}
TextureNode* Pack(TextureNode* node, const Vector2<uint32>& size)
{
if (!node->empty) {
// The node is filled, not gonna fit anything else here
assert(!node->left && !node->right);
return nullptr;
} else if (node->left && node->right) {
// Non-leaf, try inserting to the left and then to the right
TextureNode* retval = Pack(node->left.get(), size);
if (retval != nullptr) {
return retval;
}
return Pack(node->right.get(), size);
} else {
// This is an unfilled leaf - let's see if we can fill it
Vector2<uint32> realSize = node->size;
// If we're along a boundary, calculate the actual size
if (node->origin.x + node->size.x == INT_MAX) {
realSize.x = textureSize.x - node->origin.x;
}
if (node->origin.y + node->size.y == INT_MAX) {
realSize.y = textureSize.y - node->origin.y;
}
if (node->size.x == size.x && node->size.y == size.y) {
// Perfect size - just pack into this node
node->empty = false;
return node;
} else if (realSize.x < size.x || realSize.y < size.y) {
// Not big enough
return nullptr;
} else {
// Large enough - split until we get a perfect fit
TextureNode* left = nullptr;
TextureNode* right = nullptr;
// Determine how much space we'll have left if we split each way
int remainX = realSize.x - size.x;
int remainY = realSize.y - size.y;
// Split the way that will leave the most room
bool verticalSplit = remainX < remainY;
if (remainX == 0 && remainY == 0) {
// Edge case - we are are going to hit the border of
// the texture atlas perfectly, split at the border instead
if (node->size.x > node->size.y) {
verticalSplit = false;
} else verticalSplit = true;
}
if (verticalSplit) {
// Split vertically (left is top)
left = new TextureNode(node->origin, Vector2<uint32>(node->size.x, size.y));
right = new TextureNode(Vector2<uint32>(node->origin.x, node->origin.y + size.y), Vector2<uint32>(node->size.x, node->size.y - size.y));
} else {
// Split horizontally
left = new TextureNode(node->origin, Vector2<uint32>(size.x, node->size.y));
right = new TextureNode(Vector2<uint32>(node->origin.x + size.x, node->origin.y), Vector2<uint32>(node->size.x - size.x, node->size.y));
}
node->left.reset(left);
node->right.reset(right);
return Pack(node->left.get(), size);
}
}
return nullptr;
}
Vector2<uint32> PackTexture(unsigned char* textureBuffer, const Vector2<uint32>& bufferSize)
{
TextureNode* node = Pack(rootNode.get(), bufferSize);
if (node == nullptr) {
ResizeBuffer(textureSize * 2U);
node = Pack(rootNode.get(), bufferSize);
// Note: this assert will be hit if we try to pack a texture larger
// than the current size of the texture
assert(node != nullptr);
}
assert(bufferSize.x == node->size.x);
assert(bufferSize.y == node->size.y);
// Copy the texture to the texture atlas' buffer
for (uint32 ly = 0; ly < bufferSize.y; ly++) {
for (uint32 lx = 0; lx < bufferSize.x; lx++) {
int y = node->origin.y + ly;
int x = node->origin.x + lx;
buffer[y * textureSize.x + x] = textureBuffer[ly * bufferSize.x + lx];
}
}
return node->origin;
}
void ResizeBuffer(const Vector2<uint32>& newSize)
{
vector<uchar> newBuffer;
newBuffer.resize(newSize.y * newSize.x);
for (uint32 y = 0; y < textureSize.y; y++) {
for (uint32 x = 0; x < textureSize.x; x++) {
newBuffer[y * newSize.x + x] = buffer[y * textureSize.x + x];
}
}
textureSize = newSize;
buffer = std::move(newBuffer);
rootNode->size = newSize;
}
vector<uchar> buffer;
Vector2<uint32> textureSize = 512;
std::unique_ptr<TextureNode> rootNode = nullptr;
}
int main()
{
TextureAtlas atlas;
atlas.Create({256, 256};
Font font;
// size
font.Create("arial.ttf", 100.0f);
std::string chars = "ABCDEFGHIJKLMNOPRSTUWXYZabcdefghijklmnoprstuwzyw1234567890";
for(const auto& c : chars) {
auto txt = font.LoadGlyph(c);
atlas.PackTexture(txt.data(), txt.w, txt.h);
}
}
It works when the atlas space has not been changed.当图集空间没有改变时它起作用。 When there is no space left and atlas needs to be resized, the size changes, but a algorithm cannot find where to put another texture and it hits
assert(node != nullptr);
当没有剩余空间并且需要调整图集大小时,大小会发生变化,但算法无法找到放置另一个纹理的位置并且它会命中
assert(node != nullptr);
in PackTexture(..)
method.在
PackTexture(..)
方法中。 You can use whatever texture you want.你可以使用任何你想要的纹理。 The result is the same.
结果是一样的。 After running through the code by a breakpoint it looks like
rootNode
changes its size, but left
and right
still remain the same.通过断点通过代码运行后,它看起来像
rootNode
改变其大小,但left
和right
仍保持不变。 Nullptr
is returned by this step in Pack(..)
method:此步骤在
Pack(..)
方法中返回Nullptr
:
if (realSize.x < size.x || realSize.y < size.y) {
// Not big enough
return nullptr;
}
Why is that?这是为什么? How to change the algorithm, so it can handle resizing properly?
如何更改算法,以便它可以正确处理调整大小?
You haven't set the root's size properly, so the realSize
is never tweaked.你没有正确设置根的大小,所以
realSize
永远不会被调整。
Note that a default constructed TextureAtlas
is in an invalid state.请注意,默认构造的
TextureAtlas
处于无效状态。 Why have a separate Create
?为什么有一个单独的
Create
?
Also your destructor does the same thing as the default destructor.此外,您的析构函数与默认析构函数执行相同的操作。 It's better to omit it.
最好省略它。
explicit TextureAtlas(const Vector2<uint32>& size = { 512, 512 })
: textureSize(size),
rootNode(std::make_unique<TextureNode>(0, { INT_MAX, INT_MAX })),
buffer(size.w * size.h)
{
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.