#include "stdafx.h" #include "StitchSlot.h" #include "Texture.h" #include "TextureHolder.h" #include "TextureManager.h" #include "StitchedTexture.h" #include "Stitcher.h" void Stitcher::_init(const wstring &name, int maxWidth, int maxHeight, bool forcePowerOfTwo, int forcedScale) { this->name = name; this->maxWidth = maxWidth; this->maxHeight = maxHeight; this->forcePowerOfTwo = forcePowerOfTwo; this->forcedScale = forcedScale; // 4J init storageX = 0; storageY = 0; stitchedTexture = NULL; } Stitcher::Stitcher(const wstring &name, int maxWidth, int maxHeight, bool forcePowerOfTwo) { _init(name, maxWidth, maxHeight, forcePowerOfTwo, 0); } Stitcher::Stitcher(const wstring &name, int maxWidth, int maxHeight, bool forcePowerOfTwo, int forcedScale) { _init(name, maxWidth, maxHeight, forcePowerOfTwo, forcedScale); } int Stitcher::getWidth() { return storageX; } int Stitcher::getHeight() { return storageY; } void Stitcher::addTexture(TextureHolder *textureHolder) { if (forcedScale > 0) { textureHolder->setForcedScale(forcedScale); } texturesToBeStitched.insert(textureHolder); } Texture *Stitcher::constructTexture(bool mipmap) { if (forcePowerOfTwo) { storageX = smallestEncompassingPowerOfTwo(storageX); storageY = smallestEncompassingPowerOfTwo(storageY); } stitchedTexture = TextureManager::getInstance()->createTexture(name, Texture::TM_DYNAMIC, storageX, storageY, Texture::TFMT_RGBA, mipmap); stitchedTexture->fill(stitchedTexture->getRect(), 0xffff0000); vector *slots = gatherAreas(); for (int index = 0; index < slots->size(); index++) { StitchSlot *slot = slots->at(index); TextureHolder *textureHolder = slot->getHolder(); stitchedTexture->blit(slot->getX(), slot->getY(), textureHolder->getTexture(), textureHolder->isRotated()); } delete slots; TextureManager::getInstance()->registerName(name, stitchedTexture); return stitchedTexture; } void Stitcher::stitch() { //TextureHolder[] textureHolders = texturesToBeStitched.toArray(new TextureHolder[texturesToBeStitched.size()]); //Arrays.sort(textureHolders); stitchedTexture = NULL; //for (int i = 0; i < textureHolders.length; i++) for(AUTO_VAR(it, texturesToBeStitched.begin()); it != texturesToBeStitched.end(); ++it) { TextureHolder *textureHolder = *it; //textureHolders[i]; if (!addToStorage(textureHolder)) { app.DebugPrintf("Stitcher exception!\n"); #ifndef _CONTENT_PACKAGE __debugbreak(); #endif //throw new StitcherException(textureHolder); } } } vector *Stitcher::gatherAreas() { vector *result = new vector(); //for (StitchSlot slot : storage) for(AUTO_VAR(it, storage.begin()); it != storage.end(); ++it) { StitchSlot *slot = *it; slot->collectAssignments(result); } return result; } // Based on: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 int Stitcher::smallestEncompassingPowerOfTwo(int input) { int result = input - 1; result |= result >> 1; result |= result >> 2; result |= result >> 4; result |= result >> 8; result |= result >> 16; return result + 1; } bool Stitcher::addToStorage(TextureHolder *textureHolder) { for (int i = 0; i < storage.size(); i++) { if (storage.at(i)->add(textureHolder)) { return true; } // Try rotated textureHolder->rotate(); if (storage.at(i)->add(textureHolder)) { return true; } // Undo rotation textureHolder->rotate(); } return expand(textureHolder); } /** * Expand the current storage to take in account the new texture. * This should only be called if it didn't fit anywhere. * * @param textureHolder * @return Boolean indicating if it could accommodate for the growth */ bool Stitcher::expand(TextureHolder *textureHolder) { int minDistance = min(textureHolder->getHeight(), textureHolder->getWidth()); bool firstAddition = storageX == 0 && storageY == 0; // It couldn't fit, decide which direction to grow to bool growOnX; if (forcePowerOfTwo) { int xCurrentSize = smallestEncompassingPowerOfTwo(storageX); int yCurrentSize = smallestEncompassingPowerOfTwo(storageY); int xNewSize = smallestEncompassingPowerOfTwo(storageX + minDistance); int yNewSize = smallestEncompassingPowerOfTwo(storageY + minDistance); bool xCanGrow = xNewSize <= maxWidth; bool yCanGrow = yNewSize <= maxHeight; if (!xCanGrow && !yCanGrow) { return false; } // Even if the smallest side fits the larger might not >.> int maxDistance = max(textureHolder->getHeight(), textureHolder->getWidth()); // TODO: This seems wrong ... if (firstAddition && !xCanGrow && !(smallestEncompassingPowerOfTwo(storageY + maxDistance) <= maxHeight)) { return false; } bool xWillGrow = xCurrentSize != xNewSize; bool yWillGrow = yCurrentSize != yNewSize; if (xWillGrow ^ yWillGrow) { // Either grows //only pick X if it can grow AND it wanted to grow // if !xCanGrow then yCanGrow growOnX = xWillGrow && xCanGrow; } else { // Both or Neither grow -- smallest side wins growOnX = xCanGrow && xCurrentSize <= yCurrentSize; } } else { // We need to figure out to either expand bool xCanGrow = (storageX + minDistance) <= maxWidth; bool yCanGrow = (storageY + minDistance) <= maxHeight; if (!xCanGrow && !yCanGrow) { return false; } // Prefer growing on X when its: first addition *or* its the smaller of the two sides growOnX = (firstAddition || storageX <= storageY) && xCanGrow; } StitchSlot *slot; if (growOnX) { if (textureHolder->getWidth() > textureHolder->getHeight()) { textureHolder->rotate(); } // Grow the 'Y' when it has no size yet if (storageY == 0) { storageY = textureHolder->getHeight(); } int newSlotWidth = textureHolder->getWidth(); // 4J Stu - If we are expanding the texture, then allocate the full powerOfTwo size that we are going to eventually create if (forcePowerOfTwo) { newSlotWidth = smallestEncompassingPowerOfTwo(storageX + newSlotWidth) - storageX; } slot = new StitchSlot(storageX, 0, newSlotWidth, storageY); //storageX += textureHolder->getWidth(); storageX += newSlotWidth; } else { int newSlotHeight = textureHolder->getHeight(); // 4J Stu - If we are expanding the texture, then allocate the full powerOfTwo size that we are going to eventually create if (forcePowerOfTwo) { newSlotHeight = smallestEncompassingPowerOfTwo(storageY + newSlotHeight) - storageY; } // grow on Y slot = new StitchSlot(0, storageY, storageX, newSlotHeight); //storageY += textureHolder->getHeight(); storageY += newSlotHeight; } slot->add(textureHolder); storage.push_back(slot); return true; }