1041 lines
42 KiB
C++
1041 lines
42 KiB
C++
#include "stdafx.h"
|
|
|
|
#ifndef _ITF_PBKMERGER_H_
|
|
#include "AnimTexPacker/PbkMerger.h"
|
|
#endif
|
|
|
|
#ifndef _ITF_ANIMTEXPACKER_PATCH_H_
|
|
#include "AnimTexPacker/Patch.h"
|
|
#endif
|
|
|
|
#ifndef _ITF_ANIMATIONPATCHBANK_H_
|
|
#include "engine/animation/AnimationPatchBank.h"
|
|
#endif // _ITF_ANIMATIONPATCHBANK_H_
|
|
|
|
#ifndef _ITF_ERRORHANDLER_H_
|
|
#include "core/error/ErrorHandler.h"
|
|
#endif //_ITF_ERRORHANDLER_H_
|
|
|
|
#ifndef _ITF_ASSERTMANAGER_H_
|
|
#include "core/AssertManager.h"
|
|
#endif //_ITF_ASSERTMANAGER_H_
|
|
|
|
#ifndef _ITF_ANIMPACKERCONFIGURATION_H_
|
|
#include "AnimTexPacker/AnimPackerConfiguration.h"
|
|
#endif
|
|
|
|
#ifndef _ITF_FILESERVER_H_
|
|
#include "core/file/FileServer.h"
|
|
#endif //_ITF_FILESERVER_H_
|
|
|
|
#ifndef _ITF_FILEMANAGER_H_
|
|
#include "core/AdaptersInterfaces/FileManager.h"
|
|
#endif //_ITF_FILEMANAGER_H_
|
|
|
|
#ifndef _ITF_SOURCECONTROLADAPTER_H_
|
|
#include "engine/AdaptersInterfaces/SourceControlAdapter.h"
|
|
#endif // _ITF_SOURCECONTROLADAPTER_H_
|
|
|
|
#ifndef _ITF_PATCHDEPOT_H_
|
|
#include "AnimTexPacker/PatchDepot.h"
|
|
#endif
|
|
|
|
#ifndef _PACKING_TEXTURE_H_
|
|
#include "AnimTexPacker/PackingTexture.h"
|
|
#endif //_PACKING_TEXTURE_H_
|
|
|
|
#ifndef _ITF_SYSTEMADAPTER_
|
|
#include "core/AdaptersInterfaces/SystemAdapter.h"
|
|
#endif //_ITF_SYSTEMADAPTER_
|
|
|
|
#include "TexturePacker\PackerLog.h"
|
|
|
|
//#define _ONLY_SQUARE_TEXTURES
|
|
//#define SIMULATE_HD
|
|
|
|
namespace ITF
|
|
{
|
|
namespace AnimTexPacker
|
|
{
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
int getUpperPowerOf2(int x)
|
|
{
|
|
x -= 1;
|
|
x |= (x >> 1);
|
|
x |= (x >> 2);
|
|
x |= (x >> 4);
|
|
x |= (x >> 8);
|
|
x |= (x >> 16);
|
|
|
|
return x + 1;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//for sorting map by area with qsort
|
|
int mapSizeComparator(const void *_p1, const void *_p2)
|
|
{
|
|
BoxI &b1=*(BoxI*)_p1;
|
|
BoxI &b2=*(BoxI*)_p2;
|
|
|
|
int a1 = b1.maxx*b1.maxy;
|
|
int a2 = b2.maxx*b2.maxy;
|
|
if (a1<a2)
|
|
return -1;
|
|
else if (a1==a2)
|
|
{
|
|
int aspect1 = abs(b1.maxx-b1.maxy);
|
|
int aspect2 = abs(b2.maxx-b2.maxy);
|
|
if (aspect1<aspect2)
|
|
return -1;
|
|
else if (aspect1==aspect2)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
return 1;
|
|
|
|
}
|
|
//For sorting by perimeter with qsort
|
|
static int patchComparator(const void *_p1, const void *_p2)
|
|
{
|
|
Patch* patch1 = *(Patch**)_p1;
|
|
Patch* patch2 = *(Patch**)_p2;
|
|
|
|
int halfPerim1 = patch1->getSubImageWidth()+patch1->getSubImageHeight();
|
|
int halfPerim2 = patch2->getSubImageWidth()+patch2->getSubImageHeight();
|
|
|
|
if (halfPerim1<halfPerim2)
|
|
return 1;
|
|
else if (halfPerim1==halfPerim2)
|
|
return 0;
|
|
return -1;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
void PbkMerger::buildSortedPatches(SafeArray<Patch*> &_sortedPatchList)
|
|
{
|
|
_sortedPatchList.reserve(500);
|
|
|
|
ITF_LIST<PatchList*>::iterator patchListIter;
|
|
|
|
for (patchListIter=m_listOfPatchLists.begin();
|
|
patchListIter!=m_listOfPatchLists.end();
|
|
patchListIter++)
|
|
{
|
|
|
|
PatchList *patchList = *patchListIter;
|
|
ITF_LIST<Patch*>::const_iterator patchIter;
|
|
for (patchIter=patchList->m_patches.begin();
|
|
patchIter!=patchList->m_patches.end();
|
|
patchIter++)
|
|
{
|
|
_sortedPatchList.push_back(*patchIter);
|
|
}
|
|
}
|
|
|
|
if (_sortedPatchList.size())
|
|
qsort(&_sortedPatchList[0], _sortedPatchList.size(), sizeof(_sortedPatchList[0]), patchComparator);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
Patch *PbkMerger::findPatch(const StringID &_templateID)
|
|
{
|
|
ITF_LIST<PatchList*>::iterator patchListIter;
|
|
|
|
for (patchListIter=m_listOfPatchLists.begin();
|
|
patchListIter!=m_listOfPatchLists.end();
|
|
patchListIter++)
|
|
{
|
|
PatchList *patchList = *patchListIter;
|
|
|
|
ITF_LIST<Patch*>::iterator patchIter;
|
|
for (patchIter=patchList->m_patches.begin(); patchIter!=patchList->m_patches.end(); patchIter++)
|
|
{
|
|
Patch *patch = *patchIter;
|
|
if (patch->getTemplateID()==_templateID)
|
|
{
|
|
return patch;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
void PbkMerger::computeFinalMapStartupSize(int &mapWidth, int &mapHeight, int &totalArea)
|
|
{
|
|
totalArea=0;
|
|
mapWidth = 8;
|
|
mapHeight = 8;
|
|
|
|
ITF_LIST<PatchList*>::iterator patchListIter;
|
|
|
|
for (patchListIter=m_listOfPatchLists.begin();
|
|
patchListIter!=m_listOfPatchLists.end();
|
|
patchListIter++)
|
|
{
|
|
|
|
PatchList *patchList = *patchListIter;
|
|
ITF_LIST<Patch*>::const_iterator patchIter;
|
|
for (patchIter=patchList->m_patches.begin();
|
|
patchIter!=patchList->m_patches.end();
|
|
patchIter++)
|
|
{
|
|
Patch *patch = *patchIter;
|
|
if (patch->getSourceOfAlias())
|
|
continue;
|
|
|
|
int w = patch->getSubImageWidth();
|
|
int h = patch->getSubImageHeight();
|
|
totalArea += w*h;
|
|
mapWidth = Max(mapWidth, w);
|
|
mapHeight = Max(mapHeight, h);
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
void PbkMerger::createCompositePatchBank(PatchDepot *_patchDepot)
|
|
{
|
|
ITF_ASSERT(m_compositeAnimPatchBank==NULL);
|
|
ITF_LIST<PatchList*>::iterator patchListIter;
|
|
|
|
m_compositeAnimPatchBank = newAlloc(mId_Temporary, AnimPatchBank);
|
|
|
|
for (patchListIter=m_listOfPatchLists.begin(); patchListIter!=m_listOfPatchLists.end(); patchListIter++)
|
|
{
|
|
PatchList *patchList = *patchListIter;
|
|
AnimPatchBank * patchBank = patchList->m_patchBank;
|
|
|
|
|
|
for (u32 nameIndex=0; nameIndex<patchBank->m_NameToTemplate.size(); nameIndex++)
|
|
{
|
|
u32 key = patchBank->m_NameToTemplate.getKeyAt(nameIndex);
|
|
if (_patchDepot)
|
|
{
|
|
bbool isUsed = bfalse;
|
|
for (ITF_LIST<PatchList>::iterator pIt = _patchDepot->begin();
|
|
pIt != _patchDepot->end(); ++pIt)
|
|
{
|
|
PatchList & pList = *pIt;
|
|
if (find(pList.m_usedPatches.begin(), pList.m_usedPatches.end(), StringID(key)) != pList.m_usedPatches.end())
|
|
{
|
|
isUsed = btrue;
|
|
break;
|
|
}
|
|
}
|
|
if (!isUsed)
|
|
continue;
|
|
}
|
|
|
|
u32 templateInitialIndex = patchBank->m_NameToTemplate.getAt(nameIndex);
|
|
if(m_compositeAnimPatchBank->m_NameToTemplate.find(key)>=0)
|
|
{
|
|
#ifdef ITF_DEBUG_STRINGID
|
|
const String8& debugStr = patchBank->m_NameToTemplate.getDebugAt(nameIndex);
|
|
LOG("2 patch banks contain templates %s\nOne of then will be trashed", debugStr.cStr());
|
|
#else
|
|
LOG("2 patch banks have templates with the same name - One of then will be trashed");
|
|
#endif
|
|
}
|
|
u32 templateMergedIndex=m_compositeAnimPatchBank->m_templateList.size();
|
|
|
|
#ifdef ITF_DEBUG_STRINGID
|
|
const String8& debugStr = patchBank->m_NameToTemplate.getDebugAt(nameIndex);
|
|
m_compositeAnimPatchBank->m_NameToTemplate.setDBG(key, templateMergedIndex, debugStr);
|
|
#else
|
|
m_compositeAnimPatchBank->m_NameToTemplate.set(key, templateMergedIndex);
|
|
#endif
|
|
m_compositeAnimPatchBank->m_templateList.push_back(patchBank->m_templateList[templateInitialIndex]);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
void PbkMerger::destroyCompositePatchBank()
|
|
{
|
|
//Actual templates will be freed by the patch depot, so we empty the values before destroying
|
|
//to avoid freeing memory twice
|
|
if (m_compositeAnimPatchBank)
|
|
m_compositeAnimPatchBank->m_templateList.clear();
|
|
SF_DEL(m_compositeAnimPatchBank);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
void PbkMerger::buildSortedMapSizes(SafeArray<BoxI> &_sortedMapSizes, int mapWidth/*must be pow of 2*/, int mapHeight, int maxUV)
|
|
{
|
|
_sortedMapSizes.resize(0);
|
|
_sortedMapSizes.reserve(16);
|
|
|
|
for (int v=mapHeight/2; v<=maxUV; v*=2)
|
|
{
|
|
for (int u=mapWidth/2; u<=maxUV; u*=2)
|
|
{
|
|
BoxI dim(0,0,u,v);
|
|
_sortedMapSizes.push_back(dim);
|
|
}
|
|
}
|
|
|
|
if (_sortedMapSizes.size())
|
|
qsort(&_sortedMapSizes[0], _sortedMapSizes.size(), sizeof(_sortedMapSizes[0]), mapSizeComparator);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
void PbkMerger::blitFinalImage(nv::Image &newImage, int offsetX, int offsetY, const SafeArray<Patch*> &_inputPatchList, u32 _textureId)
|
|
{
|
|
TexturePacker::PackerLog::s_main->resetProgressbar(_inputPatchList.size());
|
|
for (u32 patchIndex=0; patchIndex<_inputPatchList.size(); patchIndex++)
|
|
{
|
|
Patch *patch = _inputPatchList[patchIndex];
|
|
if (!patch->getSourceOfAlias())
|
|
patch->blitToFinalPos(newImage, offsetX, offsetY, (TextureSetSubID)_textureId);
|
|
TexturePacker::PackerLog::s_main->progressbarStep();
|
|
}
|
|
}
|
|
|
|
void PbkMerger::blitFinalImageData(int mapWidth, int mapHeight, const SafeArray<Patch*> &_inputPatchList)
|
|
{
|
|
ITF_SET<AnimPatchPoint*> alreadyProcessedPoints;
|
|
|
|
TexturePacker::PackerLog::s_main->resetProgressbar(_inputPatchList.size());
|
|
for (u32 patchIndex=0; patchIndex<_inputPatchList.size(); patchIndex++)
|
|
{
|
|
Patch *patch = _inputPatchList[patchIndex];
|
|
patch->modifyUVs(mapWidth, mapHeight, alreadyProcessedPoints);
|
|
TexturePacker::PackerLog::s_main->progressbarStep();
|
|
}
|
|
|
|
SafeArray<AnimTemplate *> templateList;
|
|
templateList.reserve(_inputPatchList.size());
|
|
for (u32 patchIndex=0; patchIndex<_inputPatchList.size(); patchIndex++)
|
|
{
|
|
Patch *patch = _inputPatchList[patchIndex];
|
|
AnimTemplate *tmplt = patch->getAnimTemplate();
|
|
if (templateList.find(tmplt) >= 0)
|
|
continue;
|
|
templateList.push_back(tmplt);
|
|
tmplt->ComputeBonesLocalPos(btrue);
|
|
}
|
|
}
|
|
|
|
PackingPatch * createPackingPatchFromPatch(Patch *_patch, f32 _surface, u32 & _sampleSize)
|
|
{
|
|
if (!_patch->getPointList().size())
|
|
return NULL;
|
|
|
|
BeginEnd be;
|
|
PackingPatch *packPatch = newAlloc(mId_System, PackingPatch());
|
|
packPatch->m_points = _patch->getPointList();
|
|
be.m_begin = 0;
|
|
be.m_end = packPatch->m_points.size()-1;
|
|
packPatch->m_subPoly.push_back(be);
|
|
// Results
|
|
packPatch->computeOBB(btrue);
|
|
packPatch->sample((f32)PatchList::CURRENT_SAMPLING);
|
|
|
|
u32 nbSample = packPatch->m_widthSampling*packPatch->m_heightSampling;
|
|
_sampleSize = 0;
|
|
for (u32 xy=0; xy<nbSample; xy++)
|
|
_sampleSize += packPatch->m_sampling[xy] ? 1 : 0;
|
|
|
|
return packPatch;
|
|
}
|
|
|
|
#define LIMIT_ONE 2
|
|
#define LIMIT_TWO 130 / 100
|
|
|
|
void fillPackFromPatches(ListPatch & _initPatchs, const SafeArray<Patch*> _sortedPatchList)
|
|
{
|
|
// First check for common AABB patches
|
|
BoxI resultBox;
|
|
KeyArray<SafeArray<u32>> processedMap;
|
|
SafeArray<u32> processedList;
|
|
PackingPatch *iPackPatch = NULL;
|
|
u32 iPackPatchSize = 0;
|
|
PackingPatch *jPackPatch = NULL;
|
|
u32 jPackPatchSize = 0;
|
|
|
|
|
|
// First Check if 2 patches have same name on different images
|
|
for (u32 i=0; i<_sortedPatchList.size(); i++)
|
|
{
|
|
Patch * patchI = _sortedPatchList[i];
|
|
for (u32 j=i+1; j<_sortedPatchList.size(); j++)
|
|
{
|
|
Patch * patchJ = _sortedPatchList[j];
|
|
if (patchI->getImagePath() != patchJ->getImagePath() &&
|
|
patchI->getTemplateID() == patchJ->getTemplateID())
|
|
{
|
|
String8 imgI, imgJ;
|
|
patchI->getImagePath().toString8(imgI);
|
|
patchJ->getImagePath().toString8(imgJ);
|
|
|
|
String8 tmpName = patchI->getTemplateID().getDebugString();
|
|
String8 msg;
|
|
msg.setTextFormat("Error !!\nTwo different images:\n-%s\n%s\nhave same patch name %s",
|
|
imgI.cStr(), imgJ.cStr(), tmpName.cStr());
|
|
|
|
SYSTEM_ADAPTER->messageBox(String8("Texture Packer"), msg, 64, ITF_IDYES);
|
|
exit(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (u32 i=0; i<_sortedPatchList.size(); i++)
|
|
{
|
|
Patch * patchI = _sortedPatchList[i];
|
|
if (patchI->getSourceOfAlias())
|
|
continue;
|
|
|
|
if (processedList.find(i) >= 0)
|
|
continue;
|
|
|
|
for (u32 j=i+1; j<_sortedPatchList.size(); j++)
|
|
{
|
|
Patch * patchJ = _sortedPatchList[j];
|
|
if (patchJ->getSourceOfAlias())
|
|
continue;
|
|
|
|
if (patchI->getImagePath() != patchJ->getImagePath())
|
|
continue;
|
|
|
|
// First check for intersection
|
|
if (!patchI->getUVBox().intersect(patchJ->getUVBox(), resultBox))
|
|
continue;
|
|
|
|
int iSurf = patchI->getUVBox().surface();
|
|
int jSurf = patchJ->getUVBox().surface();
|
|
int rSurf = resultBox.surface();
|
|
|
|
// Second be sure that the intersect section is big enough
|
|
if (rSurf*LIMIT_ONE < iSurf && rSurf*LIMIT_ONE < jSurf)
|
|
continue;
|
|
|
|
// Optional - because costly
|
|
if (!iPackPatch)
|
|
{
|
|
iPackPatch = createPackingPatchFromPatch(patchI, (f32)iSurf, iPackPatchSize);
|
|
if (!iPackPatch)
|
|
continue;
|
|
}
|
|
|
|
jPackPatch = createPackingPatchFromPatch(patchJ, (f32)jSurf, jPackPatchSize);
|
|
if (!jPackPatch)
|
|
continue;
|
|
|
|
i32 xDelta = (i32)(f32(patchI->getUVBox().minx - patchJ->getUVBox().minx)/f32(PatchList::CURRENT_SAMPLING) + 0.5f);
|
|
i32 yDelta = (i32)(f32(patchI->getUVBox().miny - patchJ->getUVBox().miny)/f32(PatchList::CURRENT_SAMPLING) + 0.5f);
|
|
u32 rPackPatchSize = 0;
|
|
for (i32 y=0; y<(i32)iPackPatch->m_heightSampling; y++)
|
|
{
|
|
for (i32 x=0; x<(i32)iPackPatch->m_widthSampling; x++)
|
|
{
|
|
u32 idx = x + y*iPackPatch->m_widthSampling;
|
|
if (iPackPatch->m_sampling[idx])
|
|
{
|
|
i32 xDeltaIn = x + xDelta;
|
|
i32 yDeltaIn = y + yDelta;
|
|
if (xDeltaIn >= 0 && xDeltaIn < (i32)jPackPatch->m_widthSampling &&
|
|
yDeltaIn >= 0 && yDeltaIn < (i32)jPackPatch->m_heightSampling)
|
|
{
|
|
u32 idxDst = xDeltaIn + yDeltaIn*jPackPatch->m_widthSampling;
|
|
rPackPatchSize += jPackPatch->m_sampling[idxDst] ? 1 : 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
SF_DEL(jPackPatch);
|
|
if (rPackPatchSize*LIMIT_TWO < iPackPatchSize && rPackPatchSize*LIMIT_TWO < jPackPatchSize)
|
|
continue;
|
|
|
|
i32 idx = processedMap.find(i);
|
|
if (idx >= 0)
|
|
{
|
|
SafeArray<u32> &childs = processedMap.getAt(idx);
|
|
childs.push_back(j);
|
|
} else
|
|
{
|
|
SafeArray<u32> childs;
|
|
childs.push_back(i);
|
|
childs.push_back(j);
|
|
processedMap.set(i, childs);
|
|
processedList.push_back(i);
|
|
}
|
|
processedList.push_back(j);
|
|
}
|
|
SF_DEL(iPackPatch);
|
|
}
|
|
|
|
BeginEnd be;
|
|
for (u32 i=0; i<processedMap.size(); i++)
|
|
{
|
|
SafeArray<u32> & childs = processedMap.getAt(i);
|
|
PackingPatch paaa;
|
|
|
|
for (u32 j=0; j<childs.size(); j++)
|
|
{
|
|
const ITF_VECTOR<Vec2d> & points = _sortedPatchList[childs[j]]->getPointList();
|
|
if (points.size())
|
|
{
|
|
be.m_begin = paaa.m_points.size();
|
|
be.m_end = be.m_begin+points.size()-1;
|
|
for (u32 k=0; k<points.size(); k++)
|
|
paaa.m_points.push_back(points[k]);
|
|
|
|
paaa.m_subPoly.push_back(be);
|
|
paaa.m_srcData.push_back(childs[j]);
|
|
}
|
|
}
|
|
if (paaa.m_subPoly.size())
|
|
_initPatchs.push_back(paaa);
|
|
}
|
|
|
|
for (u32 i=0; i<_sortedPatchList.size(); i++)
|
|
{
|
|
Patch * patchI = _sortedPatchList[i];
|
|
if (patchI->getSourceOfAlias())
|
|
continue;
|
|
|
|
if (processedList.find(i) >= 0 || !patchI->getPointList().size())
|
|
continue;
|
|
|
|
PackingPatch paaa;
|
|
paaa.m_points = patchI->getPointList();
|
|
be.m_begin = 0;
|
|
be.m_end = paaa.m_points.size()-1;
|
|
paaa.m_subPoly.push_back(be);
|
|
paaa.m_srcData.push_back(i);
|
|
_initPatchs.push_back(paaa);
|
|
}
|
|
}
|
|
|
|
/////// IMAGE Resize Cf Philippe VIMONT
|
|
bbool Reduce(nv::Image & MapSRC,nv::Image & MapDST)
|
|
{
|
|
if (MapSRC.width() < MapDST.width())
|
|
return bfalse;
|
|
|
|
nv::Color32 LineMerg1[16384];
|
|
i32 PosY,LastPosY;
|
|
nv::Color32 *LAST,*DST,*SRC;
|
|
|
|
LastPosY = -1;
|
|
|
|
for (u32 SizeY = 0; SizeY < MapSRC.height();SizeY ++)
|
|
{
|
|
DST = LineMerg1;
|
|
SRC = MapSRC.scanline(SizeY);
|
|
LAST = SRC + MapSRC.width();
|
|
while (SRC < LAST) *(DST++) = *(SRC++);
|
|
|
|
u32 SizeX;
|
|
nv::Color32 *DSTO2;
|
|
SizeX = MapSRC.width();
|
|
|
|
while ((SizeX>>1) >= MapDST.width())
|
|
{
|
|
DST = DSTO2 = LineMerg1;
|
|
LAST = DST + SizeX;
|
|
while (DST < LAST)
|
|
{
|
|
*(DSTO2++) = nv::Color32(((((u32)*(DST)) & 0xfefefefe)>>1) + ((((u32)*(DST+1)) & 0xfefefefe)>>1));
|
|
DST += 2;
|
|
}
|
|
SizeX >>= 1;
|
|
}
|
|
|
|
if (SizeX != MapDST.width())
|
|
{
|
|
u32 IncX,BasX;
|
|
BasX = 0;
|
|
IncX = ( MapDST.width()<<16) / SizeX;
|
|
DST = DSTO2 = LineMerg1;
|
|
LAST = DST + SizeX;
|
|
while (DST < LAST)
|
|
{
|
|
DSTO2[BasX>>16] = *(DST++);
|
|
BasX += IncX;
|
|
}
|
|
SizeX = MapDST.width();
|
|
}
|
|
|
|
PosY = (SizeY * MapDST.height()) / MapSRC.height();
|
|
if (PosY != LastPosY)
|
|
memcpy(MapDST.scanline(PosY),LineMerg1,SizeX * 4);
|
|
else
|
|
{
|
|
DSTO2 = LineMerg1;
|
|
DST = MapDST.scanline(PosY) ;
|
|
LAST = DST + SizeX;
|
|
while (DST < LAST)
|
|
{
|
|
*(DST++) = nv::Color32(((((u32)*(DST)) & 0xfefefefe)>>1) + ((((u32)*(DSTO2)) & 0xfefefefe)>>1));
|
|
DSTO2++;
|
|
}
|
|
}
|
|
LastPosY = PosY;
|
|
}
|
|
|
|
return btrue;
|
|
}
|
|
|
|
bbool ReduceLimitedScale(nv::Image & MapSRC,nv::Image & MapDST)
|
|
{
|
|
if (MapSRC.width() < MapDST.width() || MapSRC.width() > MapDST.width()*2)
|
|
return bfalse;
|
|
|
|
// MapSRC.width() < MapDST.width()*2
|
|
double ratioX = (f32)MapSRC.width() / (f32)MapDST.width();
|
|
double ratioY = (f32)MapSRC.height() / (f32)MapDST.height();
|
|
for (u32 SizeY = 0; SizeY < MapDST.height() ;SizeY++)
|
|
{
|
|
for (u32 SizeX = 0; SizeX < MapDST.width() ;SizeX++)
|
|
{
|
|
f32 xFloat = (f32)(SizeX * ratioX);
|
|
f32 yFloat = (f32)(SizeY * ratioY);
|
|
|
|
i32 x1 = (i32)xFloat;
|
|
i32 x2 = x1 + 1;
|
|
i32 y1 = (i32)yFloat;
|
|
i32 y2 = y1 + 1;
|
|
|
|
xFloat -= (f32)x1;
|
|
yFloat -= (f32)y1;
|
|
|
|
if (x1 >= (i32)MapSRC.width() || y1 >= (i32)MapSRC.height())
|
|
continue;
|
|
|
|
if (x2 >= (i32)MapSRC.width())
|
|
{
|
|
x2 = x1;
|
|
xFloat = 0.f;
|
|
}
|
|
if (y2 >= (i32)MapSRC.height())
|
|
{
|
|
y2 = y1;
|
|
yFloat = 0.f;
|
|
}
|
|
|
|
f32 xFloatInv = 1.f - xFloat;
|
|
f32 yFloatInv = 1.f - yFloat;
|
|
|
|
|
|
nv::Color32 c1 = MapSRC.pixel(x1, y1);
|
|
nv::Color32 c2 = MapSRC.pixel(x1, y2);
|
|
nv::Color32 c3 = MapSRC.pixel(x2, y1);
|
|
nv::Color32 c4 = MapSRC.pixel(x2, y2);
|
|
|
|
MapDST.pixel(SizeX, SizeY) = nv::Color32(
|
|
u8( c1.r*xFloatInv*yFloatInv + c2.r*xFloatInv*yFloat + c3.r*xFloat*yFloatInv + c4.r*xFloat*yFloat ),
|
|
u8( c1.g*xFloatInv*yFloatInv + c2.g*xFloatInv*yFloat + c3.g*xFloat*yFloatInv + c4.g*xFloat*yFloat ),
|
|
u8( c1.b*xFloatInv*yFloatInv + c2.b*xFloatInv*yFloat + c3.b*xFloat*yFloatInv + c4.b*xFloat*yFloat ),
|
|
u8( c1.a*xFloatInv*yFloatInv + c2.a*xFloatInv*yFloat + c3.a*xFloat*yFloatInv + c4.a*xFloat*yFloat ));
|
|
}
|
|
}
|
|
return btrue;
|
|
}
|
|
|
|
|
|
void ReduceDiv2(nv::Image & MapSRC,nv::Image & MapDST)
|
|
{
|
|
nv::Color32 *LAST,*DST,*SRC,*SRC2;
|
|
for (u32 SizeY = 0; SizeY < MapDST.height() ;SizeY++)
|
|
{
|
|
DST = MapDST.scanline(SizeY);
|
|
LAST = DST + MapDST.width();
|
|
SRC = MapSRC.scanline(2*SizeY);
|
|
SRC2 = MapSRC.scanline(2*SizeY+1);
|
|
while (DST < LAST)
|
|
{
|
|
u32 A,B;
|
|
A = (((u32)*(SRC) & 0xfefefefe)>>1) + (((u32)*(SRC + 1) & 0xfefefefe)>>1);
|
|
B = (((u32)*(SRC2) & 0xfefefefe)>>1) + (((u32)*(SRC2 + 1) & 0xfefefefe)>>1);
|
|
*(DST++) = nv::Color32(((A & 0xfefefefe)>>1) + ((B & 0xfefefefe)>>1));
|
|
SRC += 2;
|
|
SRC2 += 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
bbool ReduceResO2(nv::Image & MapSRC,nv::Image & MapDST, nv::Image::Format _dstFormat)
|
|
{
|
|
u32 baseX = 8;
|
|
u32 baseY = 8;
|
|
|
|
while (baseX * 2 <= MapSRC.width())
|
|
baseX *= 2;
|
|
|
|
while (baseY * 2 <= MapSRC.height())
|
|
baseY *= 2;
|
|
|
|
nv::Image* img[2];
|
|
u32 curImg = 0;
|
|
|
|
img[curImg] = new nv::Image();
|
|
img[curImg]->setFormat(_dstFormat);
|
|
img[curImg]->allocate(baseX, baseY);
|
|
//Reduce(MapSRC, *img[curImg]);
|
|
ReduceLimitedScale(MapSRC, *img[curImg]);
|
|
|
|
while (img[curImg]->width() != MapDST.width())
|
|
{
|
|
baseX /= 2;
|
|
baseY /= 2;
|
|
|
|
img[1-curImg] = new nv::Image();
|
|
img[1-curImg]->setFormat(_dstFormat);
|
|
img[1-curImg]->allocate(baseX, baseY);
|
|
ReduceDiv2(*img[curImg], *img[1-curImg]);
|
|
|
|
delete img[curImg];
|
|
curImg = 1-curImg;
|
|
}
|
|
|
|
MapDST = *img[curImg];
|
|
delete img[curImg];
|
|
|
|
return btrue;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
bbool PbkMerger::buildPackedMap(const AnimPackerConfiguration &_config)
|
|
{
|
|
SafeArray<Patch*> sortedPatchList;
|
|
//SafeArray<BoxI> sortedMapSizes;
|
|
|
|
int minMapWidth=0, minMapHeight=0;
|
|
int totalArea=0;
|
|
computeFinalMapStartupSize(minMapWidth, minMapHeight, totalArea);
|
|
buildSortedPatches(sortedPatchList);
|
|
|
|
if (sortedPatchList.size() == 0)
|
|
return bfalse;
|
|
u32 finalWidth, finalHeight;
|
|
|
|
|
|
f32 packTextureRatio = 1.f;
|
|
if (_config.m_sampling)
|
|
PatchList::CURRENT_SAMPLING = _config.m_sampling;
|
|
|
|
if (_config.m_hdDownscaleMask & 1)
|
|
PatchList::CURRENT_SAMPLING = (u32) ((f32)PatchList::CURRENT_SAMPLING / _config.m_hdDownscale + 0.5f);
|
|
|
|
const char * fromHdStr = _config.m_hdDownscaleMask & 1 ? " from HD" : "";
|
|
String8 logStr;
|
|
f64 rs;
|
|
f32 timetocompte;
|
|
|
|
ListPatch initPatchs;
|
|
fillPackFromPatches(initPatchs, sortedPatchList);
|
|
|
|
u32 baseRatioWidth = getUpperPowerOf2(minMapWidth/PatchList::CURRENT_SAMPLING);
|
|
u32 baseRatioHeight = getUpperPowerOf2(minMapHeight/PatchList::CURRENT_SAMPLING);
|
|
PackingTexture packTexture(initPatchs, baseRatioWidth, baseRatioHeight, (f32)PatchList::CURRENT_SAMPLING);
|
|
|
|
rs = SYSTEM_ADAPTER->getTime();
|
|
packTexture.computeGrid();
|
|
timetocompte = (f32)(SYSTEM_ADAPTER->getTime()-rs);
|
|
logStr.setTextFormat("First grid process time :%0.02f s", timetocompte);
|
|
TexturePacker::PackerLog::s_main->addText(logStr.cStr());
|
|
|
|
finalWidth = packTexture.getWidth();
|
|
finalHeight = packTexture.getHeight();
|
|
// ratioMult == 2 or ratioMult == 1 because we always mult width before height
|
|
u32 ratioWH = finalWidth / finalHeight;
|
|
u32 ratioHW = finalHeight / finalWidth;
|
|
|
|
|
|
PackingTexture texturePackFinal;
|
|
bbool texturePackOk = bfalse;
|
|
u32 fillPercent = packTexture.m_minNeededGridElemts * 100 / (packTexture.getWidth()* packTexture.getHeight());
|
|
u32 fillPercentFinal= 0;
|
|
u32 tryIdx = 1;
|
|
|
|
#ifdef SIMULATE_HD
|
|
f32 beginRatioInit = (f32)packTexture.m_minNeededGridElemts / ((f32)packTexture.m_height*packTexture.m_width);
|
|
#else
|
|
if (!(_config.m_hdDownscaleMask & 1))
|
|
{
|
|
rs = SYSTEM_ADAPTER->getTime();
|
|
bbool computeOK = packTexture.packingPatch();
|
|
timetocompte = (f32)(SYSTEM_ADAPTER->getTime()-rs);
|
|
|
|
if (computeOK)
|
|
{
|
|
texturePackFinal = packTexture;
|
|
texturePackOk = btrue;
|
|
packTextureRatio = 1.f;
|
|
fillPercentFinal = fillPercent;
|
|
|
|
logStr.setTextFormat("grid %dx%d <Try %i - downscale: %.f%% > <<<SUCCEED>>> time :%0.02f s - fill percent: %d%%", PatchList::CURRENT_SAMPLING, PatchList::CURRENT_SAMPLING, 0, 0.f, timetocompte, fillPercent);
|
|
} else
|
|
{
|
|
logStr.setTextFormat("grid %dx%d <Try %i - downscale: %.f%% > <<<FAIL>>> time :%0.02f s - fill percent: %d%%", PatchList::CURRENT_SAMPLING, PatchList::CURRENT_SAMPLING, 0, 0.f, timetocompte, fillPercent);
|
|
}
|
|
TexturePacker::PackerLog::s_main->addText(logStr.cStr());
|
|
}
|
|
f32 beginRatioInit = (_config.m_hdDownscaleMask & 1) ? (f32)packTexture.m_minNeededGridElemts / ((f32)packTexture.m_height*packTexture.m_width) : 1.f;
|
|
#endif
|
|
if (beginRatioInit < 0.5f)
|
|
beginRatioInit = 0.5f;
|
|
f32 endRatioInit = 1.15f;
|
|
|
|
while (!texturePackOk)
|
|
{
|
|
f32 beginRatio = beginRatioInit;
|
|
f32 endRatio = endRatioInit;
|
|
while (endRatio - beginRatio > 0.01f )
|
|
{
|
|
f32 ratio = (endRatio + beginRatio)*0.5f;
|
|
f32 trueRatio;
|
|
rs = SYSTEM_ADAPTER->getTime();
|
|
|
|
if (ratioWH)
|
|
{
|
|
u32 newHeight = (u32)(finalHeight * ratio + 0.5f);
|
|
packTexture.reset(initPatchs, newHeight*ratioWH, newHeight, (f32)PatchList::CURRENT_SAMPLING);
|
|
trueRatio = (f32)newHeight / (f32)finalHeight;
|
|
} else
|
|
{
|
|
u32 newWidth = (u32)(finalWidth * ratio + 0.5f);
|
|
packTexture.reset(initPatchs, newWidth, newWidth*ratioHW, (f32)PatchList::CURRENT_SAMPLING);
|
|
trueRatio = (f32)newWidth / (f32)finalWidth;
|
|
}
|
|
packTexture.computeGrid();
|
|
fillPercent = packTexture.m_minNeededGridElemts * 100 / (packTexture.getWidth()* packTexture.getHeight());
|
|
|
|
bbool computeOK = bfalse;
|
|
if (fillPercentFinal <= fillPercent)
|
|
computeOK = packTexture.packingPatch();
|
|
|
|
timetocompte = (f32)(SYSTEM_ADAPTER->getTime()-rs);
|
|
|
|
if (computeOK)
|
|
{
|
|
packTextureRatio = trueRatio;
|
|
|
|
logStr.setTextFormat("grid %dx%d <Try %i - downscale: %.f%% > <<<SUCCEED>>> time :%0.02f s - fill percent: %d%%", PatchList::CURRENT_SAMPLING, PatchList::CURRENT_SAMPLING,tryIdx, (packTextureRatio-1)*100, timetocompte, fillPercent);
|
|
|
|
texturePackFinal = packTexture;
|
|
texturePackOk = btrue;
|
|
endRatio = ratio;
|
|
fillPercentFinal = fillPercent;
|
|
} else
|
|
{
|
|
logStr.setTextFormat("grid %dx%d <Try %i - downscale: %.f%% > <<<FAIL>>> time :%0.02f s - fill percent: %d%%", PatchList::CURRENT_SAMPLING, PatchList::CURRENT_SAMPLING,tryIdx, (ratio-1)*100, timetocompte, fillPercent);
|
|
beginRatio = ratio;
|
|
}
|
|
TexturePacker::PackerLog::s_main->addText(logStr.cStr());
|
|
tryIdx++;
|
|
}
|
|
if (!texturePackOk)
|
|
{
|
|
if (finalWidth <= finalHeight)
|
|
finalWidth *= 2;
|
|
else
|
|
finalHeight *= 2;
|
|
|
|
ratioWH = finalWidth / finalHeight;
|
|
ratioHW = finalHeight / finalWidth;
|
|
}
|
|
}
|
|
packTexture = texturePackFinal;
|
|
|
|
TexturePacker::PackerLog::s_main->addText("------>> Start image generation");
|
|
rs = SYSTEM_ADAPTER->getTime();
|
|
|
|
ListPatch patchs = packTexture.getPatchsList();
|
|
packTexture.recomputeRotation(patchs);
|
|
|
|
TexturePacker::PackerLog::s_main->resetProgressbar(initPatchs.size());
|
|
for (u32 patchIndex=0; patchIndex<initPatchs.size(); patchIndex++)
|
|
{
|
|
PackingPatch & packingPatch = patchs[patchIndex];
|
|
for (u32 srcIdx=0; srcIdx<packingPatch.m_srcData.size(); srcIdx++)
|
|
{
|
|
Patch *patch = sortedPatchList[packingPatch.m_srcData[srcIdx]];
|
|
|
|
if (patch->getSourceOfAlias())
|
|
continue;
|
|
BoxI result;
|
|
Transform2d uvTransform;
|
|
|
|
result = BoxI((int)packingPatch.m_translation.x(),
|
|
(int)packingPatch.m_translation.y(),
|
|
(int)packingPatch.m_translation.x()+(int)(packingPatch.m_obb.getWidth() + 0.5f),
|
|
(int)packingPatch.m_translation.y()+(int)(packingPatch.m_obb.getHeight() + 0.5f));
|
|
|
|
matrix2d rot;
|
|
f32 rotation = -packingPatch.m_rotation;
|
|
rot.set( f32_Cos(rotation), -f32_Sin(rotation),
|
|
f32_Sin(rotation), f32_Cos(rotation));
|
|
|
|
Vec2d dstPos;
|
|
Vec2d _globalDelta = packingPatch.m_points[packingPatch.m_subPoly[srcIdx].m_begin] - mulMatrix2d(rot, patch->getPointList()[0]);
|
|
dstPos = _globalDelta + mulMatrix2d(rot, Vec2d((f32)patch->getUVBox().minx, (f32)patch->getUVBox().miny) - patch->getProcessDelta());
|
|
|
|
rotation = -packingPatch.m_rotation;
|
|
uvTransform.m_rot.set( f32_Cos(rotation), -f32_Sin(rotation),
|
|
f32_Sin(rotation), f32_Cos(rotation));
|
|
uvTransform.m_pos = dstPos - packingPatch.m_translation;
|
|
patch->setAngle(rotation);
|
|
patch->setFinalPos(result, uvTransform);
|
|
patch->setSample(packingPatch.m_widthSampling, packingPatch.m_heightSampling, packingPatch.m_sampling);
|
|
}
|
|
TexturePacker::PackerLog::s_main->progressbarStep();
|
|
}
|
|
|
|
for (u32 patchIndex=0; patchIndex<sortedPatchList.size(); patchIndex++)
|
|
{
|
|
Patch *patch = sortedPatchList[patchIndex];
|
|
if (!patch->getSourceOfAlias())
|
|
continue;
|
|
patch->setFinalPosFromAlias();
|
|
}
|
|
|
|
m_grid = packTexture.m_grid;
|
|
m_gridWidth = packTexture.m_width;
|
|
m_gridHeight= packTexture.m_height;
|
|
|
|
bbool isOk=btrue;
|
|
bbool isFirst = btrue;
|
|
|
|
for (u32 textIdx=0; textIdx<(u32)TEXSET_ID_COUNT; textIdx++)
|
|
{
|
|
if (!(m_imgMask & (1<<textIdx) ))
|
|
continue;
|
|
|
|
nv::Image::Format dstFormat = nv::Image::Format_RGB;
|
|
if (m_imgUseAlphaMask & (1<<textIdx))
|
|
dstFormat = nv::Image::Format_ARGB;
|
|
|
|
SafeArray<BoxI> aabbList;
|
|
ITF_VECTOR<nv::Image> imgList;
|
|
u32 mapWidth = packTexture.getWidth()*PatchList::CURRENT_SAMPLING;
|
|
u32 mapHeight = packTexture.getHeight()*PatchList::CURRENT_SAMPLING;
|
|
u32 nbXPart = 1;
|
|
u32 nbYPart = 1;
|
|
u32 marge = 2;
|
|
if (mapWidth * mapHeight > 0x5100000)
|
|
{
|
|
u32 midWidth = mapWidth / 2;
|
|
u32 midHeight = mapHeight / 2;
|
|
aabbList.push_back(BoxI(0 , 0 , midWidth, midHeight));
|
|
aabbList.push_back(BoxI(midWidth, 0 , mapWidth, midHeight));
|
|
aabbList.push_back(BoxI(0 , midHeight , midWidth, mapHeight));
|
|
aabbList.push_back(BoxI(midWidth, midHeight , mapWidth, mapHeight));
|
|
nbXPart = 2;
|
|
nbYPart = 2;
|
|
} else
|
|
{
|
|
aabbList.push_back(BoxI(0, 0, mapWidth, mapHeight));
|
|
}
|
|
|
|
|
|
for (u32 aabbIter = 0; aabbIter < aabbList.size(); aabbIter++)
|
|
{
|
|
const BoxI & curBox = aabbList[aabbIter];
|
|
nv::Image partImage;
|
|
|
|
partImage.setFormat(dstFormat);
|
|
partImage.allocate(curBox.maxx-curBox.minx, curBox.maxy-curBox.miny);
|
|
partImage.fill(nv::Color32(0,0,0,0));
|
|
|
|
blitFinalImage(partImage, curBox.minx, curBox.miny, sortedPatchList, (TextureSetSubID)textIdx);
|
|
f32 finalRatio = ((m_hdDownscaleMask & (1<<textIdx)) ? _config.m_hdDownscale : 1.f) / packTextureRatio;
|
|
if (!isFirst)
|
|
finalRatio *= 0.5;
|
|
|
|
ITF_ASSERT(finalRatio <= 1.f);
|
|
if (finalRatio < 1.f)
|
|
{
|
|
nv::Image subImage;
|
|
subImage.setFormat(dstFormat);
|
|
|
|
u32 width = (u32)((f32)mapWidth * finalRatio) - 1; // -1 will not change pow 2 !
|
|
u32 height = (u32)((f32)mapHeight * finalRatio) - 1; // -1 will not change pow 2 !
|
|
|
|
width = getUpperPowerOf2(width) / nbXPart;
|
|
height = getUpperPowerOf2(height) / nbYPart;
|
|
subImage.allocate(width, height);
|
|
|
|
ReduceResO2(partImage, subImage, dstFormat);
|
|
|
|
partImage = subImage;
|
|
}
|
|
imgList.push_back(partImage);
|
|
}
|
|
|
|
isFirst = bfalse;
|
|
|
|
nv::Image finalImage;
|
|
if (imgList.size() == 1)
|
|
finalImage = imgList[0];
|
|
else
|
|
{
|
|
u32 finalW = 0;
|
|
u32 finalH = 0;
|
|
for (u32 x=0; x<nbXPart; x++)
|
|
finalW += imgList[x].width();
|
|
for (u32 y=0; y<nbYPart; y++)
|
|
finalH += imgList[y*nbXPart].height();
|
|
|
|
finalImage.setFormat(dstFormat);
|
|
finalImage.allocate(finalW, finalH);
|
|
i32 curPixelY = 0;
|
|
for (u32 y=0; y<nbYPart; y++)
|
|
{
|
|
i32 curPixelX = 0;
|
|
for (u32 x=0; x<nbXPart; x++)
|
|
{
|
|
nv::Image & img = imgList[x + y*nbXPart];
|
|
u32 lineSize = img.width()*4;
|
|
for (u32 yPx = 0; yPx < img.height(); yPx++)
|
|
{
|
|
nv::Color32 * srcLine = img.scanline(yPx);
|
|
nv::Color32 * dstLine = finalImage.scanline(yPx + curPixelY) + curPixelX;
|
|
ITF_Memcpy(dstLine, srcLine, lineSize);
|
|
}
|
|
curPixelX += img.width();
|
|
}
|
|
curPixelY += imgList[y*nbXPart].height();
|
|
}
|
|
}
|
|
|
|
blitFinalImageData(mapWidth, mapHeight, sortedPatchList);
|
|
|
|
|
|
|
|
bbool saved = bfalse;
|
|
String8 name;
|
|
name.setTextFormat("%s_%i.tga", getCompositeName().cStr(), textIdx);
|
|
Path imagePath = _config.m_mainPath.copyAndChangeBasename(name);
|
|
if (SOURCECONTROL_ADAPTER)
|
|
SOURCECONTROL_ADAPTER->checkout(imagePath);
|
|
|
|
String8 translated = FILESERVER->platformString8FromPath(imagePath);
|
|
saved = nv::ImageIO::save(translated.cStr(), &finalImage);
|
|
|
|
// we are not sure that the file is in perforce, so we add it
|
|
if (SOURCECONTROL_ADAPTER)
|
|
SOURCECONTROL_ADAPTER->add(imagePath);
|
|
|
|
if (!saved)
|
|
{
|
|
ITF_ERROR("Could not write %s", imagePath.toString8().cStr());
|
|
}
|
|
isOk=saved;
|
|
}
|
|
|
|
timetocompte = (f32)(SYSTEM_ADAPTER->getTime()-rs);
|
|
logStr.setTextFormat("Time to generate images%s: %0.02f s", fromHdStr, timetocompte);
|
|
TexturePacker::PackerLog::s_main->addText(logStr.cStr());
|
|
|
|
return isOk;
|
|
}
|
|
}
|
|
}
|