901 lines
36 KiB
C++
901 lines
36 KiB
C++
#include "stdafx.h"
|
|
|
|
#ifndef _ITF_ANIMTEXPACKER_PATCH_H_
|
|
#include "AnimTexPacker/Patch.h"
|
|
#endif
|
|
|
|
#ifndef _ITF_GFX_ADAPTER_H_
|
|
#include "engine/AdaptersInterfaces/GFXAdapter.h"
|
|
#endif //_ITF_GFX_ADAPTER_H_
|
|
|
|
#ifndef _ITF_ANIMATIONSKELETON_H_
|
|
#include "engine/animation/AnimationSkeleton.h"
|
|
#endif // _ITF_ANIMATIONSKELETON_H_
|
|
|
|
#ifndef _ITF_ANIMATIONPATCHBANK_H_
|
|
#include "engine/animation/AnimationPatchBank.h"
|
|
#endif // _ITF_ANIMATIONPATCHBANK_H_
|
|
|
|
#ifndef _ITF_ANIMATIONDRAW_H_
|
|
#include "engine/animation/AnimationDraw.h"
|
|
#endif // _ITF_ANIMATIONDRAW_H_
|
|
|
|
#ifndef _ITF_STATSMANAGER_H_
|
|
#include "engine/stats/statsManager.h"
|
|
#endif //_ITF_STATSMANAGER_H_
|
|
|
|
namespace ITF
|
|
{
|
|
namespace AnimTexPacker
|
|
{
|
|
u32 PatchList::CURRENT_SAMPLING = 16;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
class SimplePatch32Data
|
|
{
|
|
public:
|
|
static const int maxVertexCount=MAX_VERTEX_BY_MESH;
|
|
SimplePatch32Data() {vertexCount=0;indexCount=0;}
|
|
|
|
VertexPT vertexListPT[maxVertexCount];
|
|
u32 vertexCount;
|
|
u32 indexCount;
|
|
};
|
|
|
|
|
|
Path Patch::m_saveInFile;
|
|
u32 Patch::s_patchHdiv = PATH_HDIV_DEF;
|
|
|
|
Patch::~Patch()
|
|
{
|
|
for (u32 i=0; i<m_pixelsTab.size(); i++)
|
|
{
|
|
SF_DEL_ARRAY(m_pixelsTab[i]);
|
|
|
|
if (!m_saveInFile.isEmpty())
|
|
{
|
|
Path savePath = getPixelsTabFileName(i);
|
|
if (hwFile::exists(savePath))
|
|
hwFile::deleteFile(savePath);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
void Patch::setFinalPos(const BoxI &_finalBox, const Transform2d &_uvTransform)
|
|
{
|
|
m_finalPos = _finalBox;
|
|
m_uvlocalToRotatedLocalTransform = _uvTransform;
|
|
}
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
void Patch::setFinalPosFromAlias()
|
|
{
|
|
ITF_ASSERT(getSourceOfAlias());
|
|
m_finalPos = m_sourceOfAlias->m_finalPos;
|
|
m_uvlocalToRotatedLocalTransform = m_sourceOfAlias->m_uvlocalToRotatedLocalTransform;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
int Patch::computePlacementHeuristic(PackedTextureArea *_area, BoxI &b)
|
|
{
|
|
int x,y;
|
|
int count=0;
|
|
|
|
//upper edge
|
|
y=b.miny-1;
|
|
for (x = b.minx - 1; x <= b.maxx + 1; x++)
|
|
if (_area->testBorder(x, y))
|
|
count++;
|
|
//lower edge
|
|
y=b.maxy+1;
|
|
for (x = b.minx - 1; x <= b.maxx + 1; x++)
|
|
if (_area->testBorder(x, y))
|
|
count++;
|
|
//left edge
|
|
x = b.minx - 1;
|
|
for (y = b.miny; y <= b.maxy; y++)
|
|
if (_area->testBorder(x, y))
|
|
count++;
|
|
//right edge
|
|
x = b.maxx + 1;
|
|
for (y = b.miny; y <= b.maxy; y++)
|
|
if (_area->testBorder(x, y))
|
|
count++;
|
|
return count;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
bbool Patch::Place(PackedTextureArea *_area, BoxI &b, bbool &_rotated, Transform2d &_subImageRotation)
|
|
{
|
|
b = BoxI(0, 0, m_UVBox.maxx-m_UVBox.minx, m_UVBox.maxy-m_UVBox.miny);
|
|
int bestHeuristic = -1;
|
|
int bestx=0, besty=0;
|
|
int bestPhase = 0;
|
|
int w = getSubImageWidth();
|
|
int h = getSubImageHeight();
|
|
ITF_SET<int> placeableX, placeableY;
|
|
ITF_SET<int>::const_iterator xiter, yiter;
|
|
|
|
ITF_ASSERT(!getSourceOfAlias());
|
|
|
|
//regular orientation
|
|
_area->computeRemarkablePlacesForBox(placeableX, placeableY, w,h);
|
|
for (yiter=placeableY.begin(); yiter!=placeableY.end(); yiter++)
|
|
for (xiter = placeableX.begin(); xiter!=placeableX.end();xiter++)
|
|
{
|
|
int x = *xiter;
|
|
int y = *yiter;
|
|
b.minx = x;
|
|
b.miny = y;
|
|
b.maxx = x + w - 1;
|
|
b.maxy = y + h - 1;
|
|
int heur = computePlacementHeuristic(_area, b);
|
|
if (heur > bestHeuristic)
|
|
{
|
|
if (_area->isAreaFree(x, y, w, h))
|
|
{
|
|
|
|
bestHeuristic = heur;
|
|
bestx = x;
|
|
besty = y;
|
|
bestPhase = 0;
|
|
}
|
|
}
|
|
}
|
|
#if 0
|
|
//rotated orientation
|
|
_area->computeRemarkablePlacesForBox(placeableX, placeableY, h,w);
|
|
for (yiter=placeableY.begin(); yiter!=placeableY.end(); yiter++)
|
|
for (xiter = placeableX.begin(); xiter!=placeableX.end();xiter++)
|
|
{
|
|
int x = *xiter;
|
|
int y = *yiter;
|
|
|
|
b.minx = x;
|
|
b.miny = y;
|
|
b.maxx = x + h - 1;
|
|
b.maxy = y + w - 1;
|
|
int heur = computePlacementHeuristic(_area, b);
|
|
if (heur > bestHeuristic)
|
|
{
|
|
if (_area->isAreaFree(x, y, h, w))
|
|
{
|
|
|
|
bestHeuristic = heur;
|
|
bestx = x;
|
|
besty = y;
|
|
bestPhase = 1;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
//
|
|
if (bestPhase == 0)
|
|
{
|
|
b.minx = bestx;
|
|
b.miny = besty;
|
|
b.maxx = bestx + w - 1;
|
|
b.maxy = besty + h - 1;
|
|
_rotated = bfalse;
|
|
_subImageRotation.setIdentity();
|
|
}
|
|
else if (bestPhase == 1)
|
|
{
|
|
b.minx = bestx;
|
|
b.miny = besty;
|
|
b.maxx = bestx + h - 1;
|
|
b.maxy = besty + w - 1;
|
|
_rotated = btrue;
|
|
Vec2d nextZero(0,(f32)m_subImageHeight-1);
|
|
_subImageRotation.m_rot.set(0.f,1.f,-1.f,0.f);
|
|
_subImageRotation.m_pos = -mulMatrix2d(_subImageRotation.m_rot, nextZero);
|
|
}
|
|
else
|
|
{
|
|
ITF_ASSERT(bfalse);
|
|
}
|
|
|
|
if (bestHeuristic < 0)
|
|
return bfalse;
|
|
_area->fillBox(b);
|
|
return btrue;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
bbool Patch::isEqual(const Patch *_other)
|
|
{
|
|
if (m_diffuseImagePath!=_other->m_diffuseImagePath)
|
|
return bfalse;
|
|
if (m_UVBox != _other->m_UVBox)
|
|
return bfalse;
|
|
|
|
return btrue;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
void Patch::initialize(const StringID &_bankID, const StringID &_templateID, AnimTemplate *_animTemplate, ChainedBones &_chainedBones, ITF_VECTOR<ImageTextureMode> &_imageTab, const Path& _diffuseImagePath, AnimSkeleton *_skeleton)
|
|
{
|
|
m_bankID = _bankID;
|
|
m_diffuseImagePath = _diffuseImagePath;
|
|
m_templateID = _templateID;
|
|
m_boneIndices.resize(0);
|
|
nv::Image & firstImage = _imageTab[0].m_image;
|
|
m_initialImageWidth = (int)firstImage.width();
|
|
m_initialImageHeight = (int)firstImage.height();
|
|
m_animTemplate = _animTemplate;
|
|
m_skeleton = _skeleton;
|
|
|
|
computeUVAABB(_animTemplate, _chainedBones, firstImage);
|
|
|
|
u32 animBoneCount=_chainedBones.m_AnimBones.size();
|
|
m_boneIDs.resize(0);
|
|
m_boneIDs.reserve(animBoneCount);
|
|
m_boneIndices.reserve(animBoneCount);
|
|
|
|
for (u32 i=0; i<animBoneCount; i++)
|
|
{
|
|
AnimBone *bone = _chainedBones.m_AnimBones[i];
|
|
u32 boneIndex = (u32)_skeleton->getBoneIndex(bone->m_Name);
|
|
m_boneIndices.push_back(boneIndex);
|
|
m_boneIDs.push_back(bone->m_Name);
|
|
}
|
|
for (u32 i=0; i<_imageTab.size(); i++)
|
|
extractPixels(_imageTab[i].m_image, _imageTab[i].m_mode);
|
|
}
|
|
|
|
|
|
Path Patch::getPixelsTabFileName(u32 _idx)
|
|
{
|
|
Path filename(m_saveInFile);
|
|
String8 name;
|
|
|
|
name.setTextFormat(m_saveInFile.getBasename(), m_serialNumber, _idx);
|
|
filename.changeBasename(name);
|
|
|
|
return filename;
|
|
}
|
|
|
|
nv::Color32 * Patch::savePixelsTab(nv::Color32 * _pixelTab, u32 _idx)
|
|
{
|
|
if (m_saveInFile.isEmpty())
|
|
return _pixelTab;
|
|
|
|
hwFile* file = newAlloc(mId_Temporary, hwFile());
|
|
if ( !file->open( getPixelsTabFileName(_idx), ITF_FILE_ATTR_CREATE_NEW | ITF_FILE_ATTR_WRITE ) )
|
|
{
|
|
SF_DEL( file );
|
|
return _pixelTab;
|
|
}
|
|
|
|
file->write( _pixelTab, m_subImageHeight*m_subImageWidth*sizeof(nv::Color32));
|
|
file->close();
|
|
|
|
SF_DEL_ARRAY(_pixelTab);
|
|
SF_DEL( file );
|
|
|
|
return NULL;
|
|
}
|
|
|
|
nv::Color32 * Patch::loadPixelsTab(nv::Color32 * _pixelTab, u32 _idx)
|
|
{
|
|
if (_pixelTab)
|
|
return _pixelTab;
|
|
|
|
if (m_saveInFile.isEmpty())
|
|
return _pixelTab;
|
|
|
|
|
|
hwFile* file = newAlloc(mId_Temporary, hwFile());
|
|
if ( !file->open( getPixelsTabFileName(_idx), ITF_FILE_ATTR_READ ) )
|
|
{
|
|
SF_DEL( file );
|
|
return _pixelTab;
|
|
}
|
|
|
|
_pixelTab = newAlloc(mId_Temporary,nv::Color32[m_subImageHeight*m_subImageWidth]);
|
|
file->read( _pixelTab, m_subImageHeight*m_subImageWidth*sizeof(nv::Color32));
|
|
file->close();
|
|
SF_DEL( file );
|
|
|
|
return _pixelTab;
|
|
}
|
|
|
|
void Patch::extractPixels(nv::Image &_image, u32 _idx)
|
|
{
|
|
m_subImageWidth = getUVBoundingWidth();
|
|
m_subImageHeight = getUVBoundingHeight();
|
|
bbool useAlphaZero = (_image.format() == nv::Image::Format_RGB) && _idx > 0;
|
|
if (_idx >= m_pixelsTab.size())
|
|
{
|
|
u32 prevSize = m_pixelsTab.size();
|
|
m_pixelsTab.resize(_idx+1);
|
|
for (u32 i=prevSize; i<m_pixelsTab.size(); i++)
|
|
m_pixelsTab[i] = NULL;
|
|
}
|
|
|
|
m_pixelsTab[_idx] = newAlloc(mId_Temporary,nv::Color32[m_subImageHeight*m_subImageWidth]);
|
|
nv::Color32 emptyColor(0,0,0,0);
|
|
nv::Color32 *dest = m_pixelsTab[_idx];
|
|
for (int y=m_UVBox.miny; y<=m_UVBox.maxy; y++)
|
|
{
|
|
const nv::Color32 *c = _image.scanline(y);
|
|
bbool isYInFrame=(y>=0 && y<m_initialImageHeight);
|
|
for (int x=m_UVBox.minx; x<=m_UVBox.maxx; x++)
|
|
{
|
|
if (isYInFrame && x>=0 && x<m_initialImageWidth)
|
|
{
|
|
*dest = c[x];
|
|
if (useAlphaZero)
|
|
dest->a = 0;
|
|
dest++;
|
|
}
|
|
else
|
|
*dest++ = emptyColor;
|
|
}
|
|
}
|
|
|
|
ITF_ASSERT(dest==m_pixelsTab[_idx]+m_subImageWidth*m_subImageHeight);
|
|
m_pixelsTab[_idx] = savePixelsTab(m_pixelsTab[_idx], _idx);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
void Patch::blitToFinalPos(nv::Image &_destImage, i32 xOffset, i32 yOffset, u32 _idx)
|
|
{
|
|
ITF_ASSERT(!getSourceOfAlias());
|
|
|
|
if (!m_sampling.size())
|
|
return;
|
|
|
|
if (_idx >= m_pixelsTab.size())
|
|
return;
|
|
|
|
u32 finalW = m_widthSampling *PatchList::CURRENT_SAMPLING;
|
|
u32 finalH = m_heightSampling*PatchList::CURRENT_SAMPLING;
|
|
i32 destWidth = _destImage.width();
|
|
i32 destHeight = _destImage.height();
|
|
|
|
if ((m_finalPos.minx-xOffset) > destWidth ||
|
|
(m_finalPos.miny-yOffset) > destHeight ||
|
|
(m_finalPos.minx + finalW - xOffset) < 0 ||
|
|
(m_finalPos.maxy + finalH - yOffset) < 0)
|
|
return;
|
|
|
|
m_pixelsTab[_idx] = loadPixelsTab(m_pixelsTab[_idx], _idx);
|
|
if (!m_pixelsTab[_idx])
|
|
return;
|
|
|
|
nv::Color32 *dest = _destImage.pixels();
|
|
|
|
matrix2d invRotation;
|
|
invRotation.set(m_uvlocalToRotatedLocalTransform.m_rot.m_col1.x(),
|
|
m_uvlocalToRotatedLocalTransform.m_rot.m_col2.x(),
|
|
m_uvlocalToRotatedLocalTransform.m_rot.m_col1.y(),
|
|
m_uvlocalToRotatedLocalTransform.m_rot.m_col2.y());
|
|
Color dstColor;
|
|
for (u32 y=0; y<finalH; y++)
|
|
{
|
|
for (u32 x=0; x<finalW; x++)
|
|
{
|
|
if (!m_sampling[(x/PatchList::CURRENT_SAMPLING) + (y/PatchList::CURRENT_SAMPLING)*m_widthSampling])
|
|
continue;
|
|
Vec2d srcPos = Vec2d((f32)x-m_uvlocalToRotatedLocalTransform.m_pos.x(),
|
|
(f32)y-m_uvlocalToRotatedLocalTransform.m_pos.y());
|
|
Vec2d posUV = mulMatrix2d(invRotation, srcPos);
|
|
if (posUV.x() >= 0 && posUV.x() < m_subImageWidth &&
|
|
posUV.y() >= 0 && posUV.y() < m_subImageHeight)
|
|
{
|
|
int x0 = (int)posUV.x();
|
|
int x1 = x0 + 1;
|
|
int y0 = (int)posUV.y();
|
|
int y1 = y0 + 1;
|
|
|
|
Color c00(getPixel_WithBorder(x0,y0,_idx));
|
|
Color c10(getPixel_WithBorder(x1,y0,_idx));
|
|
Color c01(getPixel_WithBorder(x0,y1,_idx));
|
|
Color c11(getPixel_WithBorder(x1,y1,_idx));
|
|
|
|
int offsetX = x + m_finalPos.minx - xOffset;
|
|
int offsetY = y + m_finalPos.miny - yOffset;
|
|
int offset = offsetX + offsetY*destWidth;
|
|
if (offsetX >= 0 && offsetX < destWidth &&
|
|
offsetY >= 0 && offsetY < destHeight)
|
|
{
|
|
f32 xratio = posUV.x() - x0;
|
|
f32 yratio = posUV.y() - y0;
|
|
dstColor = c00*(1-xratio)*(1-yratio) +
|
|
c10*xratio*(1-yratio) +
|
|
c01*(1-xratio)*yratio +
|
|
c11*xratio*yratio;
|
|
|
|
nv::Color32 drawColor = nv::Color32(dstColor.getAsU32());
|
|
if (dest[offset].a < drawColor.a) dest[offset].a = drawColor.a;
|
|
if (dest[offset].r < drawColor.r) dest[offset].r = drawColor.r;
|
|
if (dest[offset].g < drawColor.g) dest[offset].g = drawColor.g;
|
|
if (dest[offset].b < drawColor.b) dest[offset].b = drawColor.b;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!m_saveInFile.isEmpty())
|
|
{
|
|
SF_DEL_ARRAY(m_pixelsTab[_idx]);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
void Patch::growPatchMaxSize(const BoneMaxSize& _toAdd )
|
|
{
|
|
m_boneMaxSize.grow(_toAdd.m_worldSize.getMax(), _toAdd.m_uvSize.getMax());
|
|
}
|
|
|
|
const BoneMaxSize &Patch::getPatchMaxSize() const
|
|
{
|
|
ITF_ASSERT(!getSourceOfAlias());
|
|
return m_boneMaxSize;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//void Patch::resizeTexture(int _newSize)
|
|
//{
|
|
// ITF_ASSERT(!getSourceOfAlias());
|
|
// int w = getSubImageWidth();
|
|
// int h = getSubImageHeight();
|
|
// if (w>=h)
|
|
// {
|
|
// float ratio = h/(float)w;
|
|
// w = _newSize;
|
|
// h = (int) (_newSize*ratio);
|
|
// if (h<1)
|
|
// h = 1;
|
|
// }
|
|
// else
|
|
// {
|
|
// float ratio = w/(float)h;
|
|
// h = _newSize;
|
|
// w = (int) (_newSize*ratio);
|
|
// if (w<1)
|
|
// w = 1;
|
|
// }
|
|
// int srcWidth = getSubImageWidth();
|
|
// int srcHeight = getSubImageHeight();
|
|
|
|
// m_imageStretchTransform.m_rot.set(w/(f32)srcWidth, 0,0, h/(f32)srcHeight);
|
|
// m_imageStretchTransform.m_pos.set(0,0);
|
|
|
|
// ITF_ASSERT(w<srcWidth && h<srcHeight);
|
|
|
|
// u32 imgCount = m_pixelsTab.size();
|
|
// for (u32 imgIdx=0; imgIdx<imgCount; imgIdx++)
|
|
// {
|
|
// nv::Color32 *newPixels = new nv::Color32[w*h];
|
|
// for (int y=0; y<h; y++)
|
|
// {
|
|
// for (int x=0; x<w; x++)
|
|
// {
|
|
// f32 xInSource_Start = x*(srcWidth/(f32)w);
|
|
// f32 xInSource_End = (x+1)*(srcWidth/(f32)w);
|
|
// xInSource_End = Min(xInSource_End, (f32)srcWidth);
|
|
// f32 yInSource_Start = y*(srcHeight/(f32)h);
|
|
// f32 yInSource_End = (y+1)*(srcHeight/(f32)h);
|
|
// yInSource_End = Min(yInSource_End, (f32)srcHeight);
|
|
// int pixelCount = 0;
|
|
// int pixelR=0, pixelG=0, pixelB=0, pixelA=0;
|
|
|
|
// for (float subY=yInSource_Start; subY<yInSource_End; subY = floorf(subY)+1.f)
|
|
// {
|
|
// for (float subX=xInSource_Start; subX<xInSource_End; subX = floorf(subX)+1.f)
|
|
// {
|
|
// nv::Color32 c = samplePixel_Linear(subX, subY, imgIdx);
|
|
// pixelR += c.r;
|
|
// pixelG += c.g;
|
|
// pixelB += c.b;
|
|
// pixelA += c.a;
|
|
// pixelCount++;
|
|
// }
|
|
// }
|
|
|
|
// nv::Color32 mean(0,0,0,0);
|
|
// ITF_ASSERT(pixelCount!=0);
|
|
// if (pixelCount!=0)
|
|
// {
|
|
// mean.r = (u8)(pixelR/pixelCount);
|
|
// mean.g = (u8)(pixelG/pixelCount);
|
|
// mean.b = (u8)(pixelB/pixelCount);
|
|
// mean.a = (u8)(pixelA/pixelCount);
|
|
// }
|
|
|
|
// newPixels[x+y*w] = mean;
|
|
|
|
// }
|
|
// }
|
|
// //
|
|
// delete [] m_pixelsTab[imgIdx];
|
|
// m_pixelsTab[imgIdx] = newPixels;
|
|
// }
|
|
// m_subImageWidth = w;
|
|
// m_subImageHeight = h;
|
|
//}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
nv::Color32 Patch::samplePixel_Linear(f32 _x, f32 _y, u32 _idx)
|
|
{
|
|
f32 xInSource = _x;
|
|
f32 yInSource = _y;
|
|
double dummyDouble;
|
|
int x0 = (int)xInSource;
|
|
int srcWidth = (int)getSubImageWidth();
|
|
int srcHeight = (int)getSubImageHeight();
|
|
int x1 = Min(srcWidth-1, x0+1);
|
|
int y0 = (int)yInSource;
|
|
int y1 = Min(srcHeight-1, y0+1);
|
|
f32 fracX = (float)modf(xInSource, &dummyDouble);
|
|
f32 fracY = (float)modf(yInSource, &dummyDouble);
|
|
f32 invFracX = 1-fracX;
|
|
f32 invFracY = 1-fracY;
|
|
nv::Color32 p00 = getPixel(x0,y0,_idx);
|
|
nv::Color32 p10 = getPixel(x1,y0,_idx);
|
|
nv::Color32 p01 = getPixel(x0,y1,_idx);
|
|
nv::Color32 p11 = getPixel(x1,y1,_idx);
|
|
float w00 = invFracX*invFracY;
|
|
float w10 = fracX*invFracY;
|
|
float w01 = invFracX*fracY;
|
|
float w11 = fracX*fracY;
|
|
|
|
int r = (int)(p00.r*w00 + p10.r*w10 + p01.r*w01 + p11.r*w11);
|
|
int g = (int)(p00.g*w00 + p10.g*w10 + p01.g*w01 + p11.g*w11);
|
|
int b = (int)(p00.b*w00 + p10.b*w10 + p01.b*w01 + p11.b*w11);
|
|
int a = (int)(p00.a*w00 + p10.a*w10 + p01.a*w01 + p11.a*w11);
|
|
|
|
return nv::Color32((u8)r,(u8)g,(u8)b,(u8)a);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
void Patch::modifyUVs(int mapWidth, int mapHeight, ITF_SET<AnimPatchPoint*> &_alreadyProcessedPoints)
|
|
{
|
|
if (m_uvModified)
|
|
return;
|
|
|
|
m_uvModified = btrue;
|
|
|
|
//Compute, for UV space : OriginalUV->truncatedInteger->rotatedInteger->placedInteger->final
|
|
Transform2d originalToTruncated, truncatedRotatedToStretched, truncatedToRotated, rotatedToPlaced, placedToFinal;
|
|
|
|
originalToTruncated.m_rot.set((f32)m_initialImageWidth, 0, 0, (f32)m_initialImageWidth);
|
|
originalToTruncated.m_pos = - Vec2d((f32)m_UVBox.minx, (f32)m_UVBox.miny);
|
|
|
|
Patch *transformSource = getSourceOfAlias();
|
|
if (!transformSource)
|
|
transformSource = this;
|
|
truncatedToRotated = transformSource->m_uvlocalToRotatedLocalTransform;
|
|
truncatedRotatedToStretched = transformSource->m_imageStretchTransform;
|
|
|
|
rotatedToPlaced.m_rot.setIdentity();
|
|
rotatedToPlaced.m_pos = Vec2d((f32)m_finalPos.minx+0.5f, (f32)m_finalPos.miny+0.5f);
|
|
|
|
placedToFinal.m_rot.set(1.f/mapWidth, 0, 0, 1.f/mapWidth);
|
|
placedToFinal.m_pos.set(0,0);
|
|
|
|
Transform2d uvFinalTransform = placedToFinal*rotatedToPlaced*truncatedRotatedToStretched*truncatedToRotated*originalToTruncated;
|
|
|
|
//we compute an uniform tranform, which doesnt take whole texture shapes, because rectangular textures can make UV squashed
|
|
Transform2d uvVectorTranformUniform = rotatedToPlaced*truncatedRotatedToStretched*truncatedToRotated;
|
|
|
|
//We modify Pos, PosEnd for each bone seed (hierarchy start). Seed is at index 0 for m_boneIndices
|
|
for (u32 boneIndexInList=0; boneIndexInList<m_boneIDs.size(); boneIndexInList++)
|
|
{
|
|
i32 boneIDIndex = m_animTemplate->m_NameToBone.find(m_boneIDs[boneIndexInList]);
|
|
i32 boneIndex = -1;
|
|
if (boneIDIndex>=0)
|
|
boneIndex = m_animTemplate->m_NameToBone.getAt(boneIDIndex);
|
|
ITF_ASSERT(boneIndex>=0 && boneIndex<(i32)m_animTemplate->m_BonesCurFrameDyn.size());
|
|
AnimBoneDyn *seedBone = &m_animTemplate->m_BonesCurFrameDyn[boneIndex];
|
|
ITF_ASSERT(seedBone);
|
|
|
|
//f32 localAngleRad = seedBone->m_AngleLocal;
|
|
//f32 localAngleRad = seedBone->m_Angle;
|
|
|
|
if (boneIndexInList==0)
|
|
{
|
|
seedBone->m_Pos = mulTransform2d(uvFinalTransform, seedBone->m_Pos);
|
|
seedBone->m_Angle += transformSource->m_angle;
|
|
}
|
|
|
|
//the following code may have problems with non-square textures or rotation. we should use X ratio instead
|
|
Vec2d boneLenVec(seedBone->m_Scale.x(), 0);
|
|
boneLenVec = mulMatrix2d(uvFinalTransform.m_rot, boneLenVec);
|
|
seedBone->m_Scale.x() = boneLenVec.norm();
|
|
|
|
}
|
|
|
|
//each AnimTemplate is broken in several Patch,
|
|
//so we need to iterate each AnimPatch in it to check it's connected to a bone related to the Patch
|
|
//If so, we can modify the points in the AnimPatch
|
|
for (u32 patchIndex=0; patchIndex<m_animTemplate->m_Patch.size(); patchIndex++)
|
|
{
|
|
AnimPatch *patch = &m_animTemplate->m_Patch[patchIndex];
|
|
if (patch->m_nbPatchPoints!=0)
|
|
{
|
|
AnimBone *boneToFind = patch->m_AnimPatchPoints[0]->m_mainRefs.m_BonePtr;
|
|
u32 boneIndex=0;
|
|
ITF_ASSERT(boneToFind);
|
|
StringID idToFind = boneToFind->m_Name;
|
|
|
|
//In the bone in current patch list?
|
|
bbool isFound = bfalse;
|
|
for (boneIndex=0; boneIndex<m_boneIDs.size(); boneIndex++)
|
|
{
|
|
if (idToFind==m_boneIDs[boneIndex])
|
|
{
|
|
isFound = btrue;
|
|
|
|
|
|
break;
|
|
}
|
|
}
|
|
//if we are in right AnimPatch
|
|
if (isFound)
|
|
{
|
|
//
|
|
//modify points
|
|
for (u32 pointIndex=0; pointIndex<patch->m_nbPatchPoints; pointIndex++)
|
|
{
|
|
AnimPatchPoint *p = patch->m_AnimPatchPoints[pointIndex];
|
|
if (find(_alreadyProcessedPoints.begin(), _alreadyProcessedPoints.end(), p)!=_alreadyProcessedPoints.end())
|
|
continue;
|
|
_alreadyProcessedPoints.insert(p);
|
|
|
|
p->m_PosUV = mulTransform2d(uvFinalTransform, p->m_PosUV);
|
|
p->m_NormaleUV = mulMatrix2d(uvVectorTranformUniform.m_rot, p->m_NormaleUV);
|
|
p->m_NormaleUV.normalize();
|
|
|
|
//the following code may have problems with non-square textures or rotation. we should use Y ratio instead
|
|
Vec2d boneLenVec(0, f32_Abs(p->m_mainRefs.m_Pos.y()));
|
|
boneLenVec = mulMatrix2d(uvFinalTransform.m_rot, boneLenVec);
|
|
p->m_mainRefs.m_Pos.y() = boneLenVec.norm()*getSign(p->m_mainRefs.m_Pos.y());
|
|
//p->m_mainRefs.m_Pos.y() *= initialToFinalHeight;
|
|
|
|
//p->m_mainRefs.m_Normale = mulMatrix2d(uvVectorTranformUniform.m_rot, p->m_mainRefs.m_Normale);
|
|
//p->m_mainRefs.m_Normale.normalize();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
bbool Patch::computePatchUVAABB(AnimPatch *_patch, AABB &UVs, nv::Image &_image, Vec2d *_ptList)
|
|
{
|
|
struct TexDesc
|
|
{
|
|
u32 m_sizeX, m_sizeY;
|
|
f32 getRatioXY() {return (f32) m_sizeX/(f32) m_sizeY;}
|
|
} textureDesc;
|
|
UVs = AABB(Vec2d(0,0));
|
|
bbool isBoxInit=bfalse;
|
|
TexDesc *texture = &textureDesc;
|
|
SimplePatch32Data data;
|
|
SimplePatch32Data *_pData = &data;
|
|
Vec2d PointsNS[16];
|
|
const AnimPatchPointDyn *ppDynTab[4];
|
|
const AnimPatchPoint *ppTab[4];
|
|
AnimPatchPointDyn dummyDyn[4];
|
|
const u32 _hdiv = s_patchHdiv;
|
|
const u32 _vdiv = 1;
|
|
const f32 _z = 0;
|
|
textureDesc.m_sizeX = _image.width();
|
|
textureDesc.m_sizeY = _image.height();
|
|
ITF_MemSet(dummyDyn, 0, sizeof(dummyDyn));
|
|
|
|
if (_patch->m_nbPatchPoints == 4)
|
|
{
|
|
ppTab[0] = _patch->m_AnimPatchPoints[1];
|
|
ppTab[1] = _patch->m_AnimPatchPoints[3];
|
|
ppTab[2] = _patch->m_AnimPatchPoints[0];
|
|
ppTab[3] = _patch->m_AnimPatchPoints[2];
|
|
} else
|
|
{
|
|
ITF_ASSERT(_patch->m_nbPatchPoints == 2);
|
|
ppTab[0] = _patch->m_AnimPatchPoints[0];
|
|
ppTab[1] = _patch->m_AnimPatchPoints[1];
|
|
ppTab[2] = _patch->m_AnimPatchPoints[1];
|
|
ppTab[3] = _patch->m_AnimPatchPoints[0];
|
|
}
|
|
|
|
ppDynTab[0] = &dummyDyn[ppTab[0]->m_Index];
|
|
ppDynTab[1] = &dummyDyn[ppTab[1]->m_Index];
|
|
ppDynTab[2] = &dummyDyn[ppTab[2]->m_Index];
|
|
ppDynTab[3] = &dummyDyn[ppTab[3]->m_Index];
|
|
|
|
AnimDraw::computePatchControlPoints(&PointsNS[0],ppDynTab, ppTab, _hdiv, _vdiv, bfalse);
|
|
#undef PATCH32_USE_PCT
|
|
#undef PATCH32_USE_COLOR
|
|
#define PATCH32_DO_NOT_RETURN
|
|
#define PATCH32_IGNORE_MESHELEMENTS
|
|
#include "engine/AdaptersInterfaces/GFXAdapter_DrawPatch.h"
|
|
|
|
VertexPT * vertices = _pData->vertexListPT;
|
|
for (u32 vertIndex=0; vertIndex<_pData->vertexCount; vertIndex++)
|
|
{
|
|
_ptList[vertIndex] = vertices[vertIndex].m_uv;
|
|
if (!isBoxInit)
|
|
{
|
|
UVs = AABB(_ptList[vertIndex]);
|
|
isBoxInit = btrue;
|
|
}
|
|
else
|
|
{
|
|
UVs.grow(_ptList[vertIndex]);
|
|
}
|
|
}
|
|
ITF_ASSERT(_pData->vertexCount<SimplePatch32Data::maxVertexCount);
|
|
return isBoxInit;
|
|
}
|
|
|
|
struct SegmentPatch
|
|
{
|
|
i32 m_beginIndex;
|
|
i32 m_endIndex;
|
|
i32 m_nextIndex;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
///compute uv aabb from animation data
|
|
void Patch::computeUVAABB(AnimTemplate *_template, ChainedBones &_chainedBones, nv::Image &_image)
|
|
{
|
|
AABB UVs(Vec2d(0,0));
|
|
bbool isUVInit=bfalse;
|
|
u32 lineSize = (s_patchHdiv+1);
|
|
u32 pathSize = lineSize*2;
|
|
|
|
SafeArray<Vec2d> pointListTmp1;
|
|
SafeArray<Vec2d> pointListTmp2;
|
|
|
|
for (int boneIndex=0; boneIndex<(int)_chainedBones.m_AnimBones.size(); boneIndex++)
|
|
{
|
|
AnimBone *bone = _chainedBones.m_AnimBones[boneIndex];
|
|
|
|
//for each patch linked to this bone
|
|
for (u32 patchIndex=0; patchIndex<_template->m_Patch.size(); patchIndex++)
|
|
{
|
|
AnimPatch *patch = &_template->m_Patch[patchIndex];
|
|
if (patch->m_nbPatchPoints!=0 &&
|
|
patch->m_AnimPatchPoints[0]->m_mainRefs.m_BonePtr == bone)
|
|
{
|
|
AABB patchBB;
|
|
u32 begin = pointListTmp1.size();
|
|
pointListTmp1.resize(begin + pathSize);
|
|
if (computePatchUVAABB(patch, patchBB, _image, &pointListTmp1[begin]))
|
|
{
|
|
if (isUVInit)
|
|
{
|
|
UVs.grow(patchBB);
|
|
}
|
|
else
|
|
{
|
|
UVs = patchBB;
|
|
isUVInit = btrue;
|
|
}
|
|
}
|
|
//
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// now we recompute the pointList
|
|
i32 nbPatchs = pointListTmp1.size()/pathSize;
|
|
ITF_VECTOR<SegmentPatch> segmentList;
|
|
for (i32 i=0; i<nbPatchs; i++)
|
|
{
|
|
SegmentPatch spatch;
|
|
spatch.m_beginIndex = i*pathSize;
|
|
spatch.m_endIndex = i*pathSize + lineSize-1;
|
|
spatch.m_nextIndex = i*pathSize + pathSize-1;
|
|
segmentList.push_back(spatch);
|
|
|
|
spatch.m_beginIndex = i*pathSize + pathSize-1;
|
|
spatch.m_endIndex = i*pathSize + lineSize;
|
|
spatch.m_nextIndex = i*pathSize;
|
|
segmentList.push_back(spatch);
|
|
}
|
|
|
|
Vec2d posTab[2];
|
|
if (segmentList.size())
|
|
{
|
|
for (u32 i=0; i<segmentList.size()-1; i++)
|
|
{
|
|
posTab[0] = pointListTmp1[segmentList[i].m_endIndex];
|
|
posTab[1] = pointListTmp1[segmentList[i].m_nextIndex];
|
|
|
|
for (u32 posIdx=0; posIdx<2; posIdx++)
|
|
{
|
|
Vec2d & pos2 = posTab[posIdx];
|
|
u32 j;
|
|
for (j=i+1; j<segmentList.size(); j++)
|
|
{
|
|
if (pos2.IsEqual(pointListTmp1[segmentList[j].m_beginIndex], MTH_EPSILON))
|
|
{
|
|
SegmentPatch spatch = segmentList[j];
|
|
segmentList[j] = segmentList[i+1];
|
|
segmentList[i+1] = spatch;
|
|
break;
|
|
} else if (pos2.IsEqual(pointListTmp1[segmentList[j].m_endIndex], MTH_EPSILON))
|
|
{
|
|
SegmentPatch spatch = segmentList[j];
|
|
segmentList[j] = segmentList[i+1];
|
|
u32 swapIdx = spatch.m_beginIndex;
|
|
spatch.m_beginIndex = spatch.m_endIndex;
|
|
spatch.m_endIndex = swapIdx;
|
|
segmentList[i+1] = spatch;
|
|
break;
|
|
}
|
|
}
|
|
// no equal found next patch
|
|
if (j != segmentList.size())
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
pointListTmp2.reserve(pointListTmp1.size());
|
|
for (u32 i=0; i<segmentList.size(); i++)
|
|
{
|
|
SegmentPatch &spatch = segmentList[i];
|
|
if (spatch.m_beginIndex < spatch.m_endIndex)
|
|
{
|
|
for (i32 j=spatch.m_beginIndex; j<=spatch.m_endIndex; j++)
|
|
if (!pointListTmp2.size() || !pointListTmp2[pointListTmp2.size()-1].IsEqual(pointListTmp1[j], MTH_EPSILON))
|
|
pointListTmp2.push_back(pointListTmp1[j]);
|
|
} else
|
|
{
|
|
for (i32 j=spatch.m_beginIndex; j>=spatch.m_endIndex; j--)
|
|
if (!pointListTmp2.size() || !pointListTmp2[pointListTmp2.size()-1].IsEqual(pointListTmp1[j], MTH_EPSILON))
|
|
pointListTmp2.push_back(pointListTmp1[j]);
|
|
}
|
|
}
|
|
|
|
|
|
m_processDelta = Vec2d::Zero;
|
|
m_pointList.resize(pointListTmp2.size());
|
|
for (u32 i=0; i<m_pointList.size(); i++)
|
|
{
|
|
m_pointList[i].x() = pointListTmp2[i].x()*(f32)m_initialImageWidth;
|
|
m_pointList[i].y() = pointListTmp2[i].y()*(f32)m_initialImageHeight;
|
|
if (m_processDelta.x() > m_pointList[i].x())
|
|
m_processDelta.x() = m_pointList[i].x();
|
|
if (m_processDelta.y() > m_pointList[i].y())
|
|
m_processDelta.y() = m_pointList[i].y();
|
|
}
|
|
for (u32 i=0; i<m_pointList.size(); i++)
|
|
{
|
|
m_pointList[i] -= m_processDelta;
|
|
}
|
|
|
|
int width = _image.width();
|
|
int height = _image.height();
|
|
//knowing we have bilinear filtering, we get every pixel concerned
|
|
UVs.setMin(UVs.getMin()*Vec2d((f32)width, (f32)height)-Vec2d(0.5f,0.5f));
|
|
UVs.setMax(UVs.getMax()*Vec2d((f32)width, (f32)height)+Vec2d(0.5f,0.5f));
|
|
|
|
m_UVBox.minx = (int)floorf(UVs.getMin().x());
|
|
m_UVBox.miny = (int)floorf(UVs.getMin().y());
|
|
m_UVBox.maxx = (int)ceilf(UVs.getMax().x());
|
|
m_UVBox.maxy = (int)ceilf(UVs.getMax().y());
|
|
|
|
}
|
|
|
|
void Patch::setSample( u32 _width, u32 _height, const SafeArray<u32> & _sampling )
|
|
{
|
|
m_widthSampling = _width;
|
|
m_heightSampling = _height;
|
|
m_sampling = _sampling;
|
|
}
|
|
|
|
|
|
}
|
|
}
|