559 lines
No EOL
14 KiB
C++
559 lines
No EOL
14 KiB
C++
#include "stdafx.h"
|
|
|
|
#ifndef _PACKING_PATCH_H_
|
|
#include "AnimTexPacker/PackingPatch.h"
|
|
#endif //_PACKING_PATCH_H_
|
|
|
|
#ifndef _ITF_SEEDER_H_
|
|
#include "core/Seeder.h"
|
|
#endif //_ITF_SEEDER_H_
|
|
|
|
#ifndef _ITF_PACKEDTEXTUREAREA_H_
|
|
#include "AnimTexPacker/PackedTextureArea.h"
|
|
#endif //_ITF_PACKEDTEXTUREAREA_H_
|
|
|
|
|
|
namespace ITF
|
|
{
|
|
|
|
//*****************************************************************************
|
|
|
|
void seedFill(SafeArray<u32> & _sample, i32 x, i32 y, u32 _width, u32 _height, u32 fillidx)
|
|
{
|
|
|
|
const u32 maxSample = _width * _height;
|
|
|
|
list<Vec2d> liste_pixel;
|
|
i32 xCourant, yCourant, xVoisin, yVoisin;
|
|
|
|
Vec2d coordTemp;
|
|
coordTemp.x() = (f32)x;
|
|
coordTemp.y() = (f32)y;
|
|
|
|
liste_pixel.push_back(coordTemp);
|
|
|
|
while (!liste_pixel.empty())
|
|
{
|
|
|
|
// On traite le pixel courant
|
|
xCourant = (i32)(liste_pixel.front().x());
|
|
yCourant = (i32)(liste_pixel.front().y());
|
|
|
|
// Remplir le pixel courant du masque passé en paramétre
|
|
// (-1 : présent dans la file, 0 : vide, 1 : rempli)
|
|
//masque.m_masque[xCourant][yCourant] = 1;
|
|
i32 index = y * _width + x;
|
|
ITF_ASSERT(index < (i32)maxSample);
|
|
_sample[index] = fillidx+1;
|
|
|
|
|
|
// On ajoute les voisins
|
|
i32 xTab[4] = { xCourant , xCourant+1, xCourant , xCourant-1};
|
|
i32 yTab[4] = { yCourant-1, yCourant , yCourant+1, yCourant };
|
|
|
|
for (int ptIdx=0; ptIdx<4; ptIdx++)
|
|
{
|
|
xVoisin = xTab[ptIdx];
|
|
yVoisin = yTab[ptIdx];
|
|
|
|
if ( xVoisin >= 0 && xVoisin < (i32)_width && yVoisin >= 0 && yVoisin < (i32)_height)
|
|
{
|
|
u32 indexVoisin = yVoisin * _width + xVoisin;
|
|
// Note : attention, on utilise ici les propriétés du && parésseux !
|
|
u32 samplingVoisin = _sample[indexVoisin];
|
|
if (((samplingVoisin & SAMPLE_MASK) != fillidx) && (samplingVoisin != (fillidx+1)))
|
|
{
|
|
_sample[indexVoisin] = fillidx+1;
|
|
coordTemp.x() = (f32)xVoisin;
|
|
coordTemp.y() = (f32)yVoisin;
|
|
liste_pixel.push_back(coordTemp);
|
|
}
|
|
}
|
|
}
|
|
|
|
// On a fini de traiter le pixel courant on l'enléve de la liste
|
|
liste_pixel.pop_front();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//*****************************************************************************
|
|
|
|
|
|
|
|
//*****************************************************************************
|
|
PackingPatch::PackingPatch()
|
|
: m_area(0.0f)
|
|
, m_angle(0.0f)
|
|
, m_baryCenter(Vec2d::Zero)
|
|
, m_widthSampling(0)
|
|
, m_heightSampling(0)
|
|
, m_posInImage(Vec2d::Zero)
|
|
, m_rotateMode(ROTATE_0)
|
|
, m_rotation(0.0f)
|
|
, m_translation(Vec2d::Zero)
|
|
{
|
|
// none
|
|
}
|
|
|
|
//*****************************************************************************
|
|
|
|
PackingPatch::~PackingPatch()
|
|
{
|
|
// none
|
|
}
|
|
|
|
|
|
//*****************************************************************************
|
|
|
|
void PackingPatch::computeOBB(bbool _ignoreRotate)
|
|
{
|
|
f32 angleStep = MTH_PIBY2 / SAMPLE_ROTATE_OOBB;
|
|
Vec2d baryCenter = getBaryCenter();
|
|
|
|
f32 minArea = F32_INFINITY;
|
|
AABB minAABB(Vec2d::Zero, Vec2d::Zero);
|
|
f32 minAngle = 0.0f;
|
|
|
|
const u32 countPoints = m_points.size();
|
|
|
|
for (u32 i = 0; i < SAMPLE_ROTATE_OOBB; i++)
|
|
{
|
|
Vec2d min = Vec2d(F32_INFINITY, F32_INFINITY);
|
|
Vec2d max = Vec2d(-F32_INFINITY, -F32_INFINITY);
|
|
|
|
const f32 angle = angleStep * i;
|
|
|
|
// Rotate all points
|
|
for (u32 j = 0; j < countPoints; j++)
|
|
{
|
|
Vec2d point = m_points[j]; //copy
|
|
|
|
point = point.RotateAround(baryCenter, angle);
|
|
|
|
min.x() = Min(min.x(), point.x());
|
|
min.y() = Min(min.y(), point.y());
|
|
max.x() = Max(max.x(), point.x());
|
|
max.y() = Max(max.y(), point.y());
|
|
}
|
|
|
|
|
|
AABB aabb(min, max);
|
|
f32 area = aabb.getArea();
|
|
|
|
if (minArea > area)
|
|
{
|
|
minArea = area;
|
|
minAABB = aabb;
|
|
minAngle = angle;
|
|
}
|
|
|
|
if (_ignoreRotate)
|
|
break;
|
|
}
|
|
|
|
// Results
|
|
m_area = minArea;
|
|
m_angle = minAngle;
|
|
m_obb = minAABB;
|
|
m_baryCenter = baryCenter;
|
|
|
|
// Rotate def points
|
|
for (u32 j = 0; j < countPoints; j++)
|
|
{
|
|
Vec2d& point = m_points[j]; // Ref
|
|
point = point.RotateAround(m_baryCenter, m_angle);
|
|
}
|
|
|
|
// Move all in 0,0
|
|
for (u32 j = 0; j < countPoints; j++)
|
|
{
|
|
Vec2d& point = m_points[j];
|
|
point -= m_obb.getMin();
|
|
}
|
|
m_obb.moveCenter(m_obb.getCenter() - m_obb.getMin());
|
|
m_baryCenter -= m_obb.getMin();
|
|
}
|
|
|
|
//*****************************************************************************
|
|
|
|
|
|
bbool PackingPatch::isInside(f32 _gridSample, u32 fillIdx, u32 idxBegin, u32 idxEnd, u32 _x, u32 _y)
|
|
{
|
|
u32 indexRandom = _y * m_widthSampling + _x;
|
|
if ((m_sampling[indexRandom] & SAMPLE_MASK) == fillIdx)
|
|
{
|
|
return bfalse;
|
|
}
|
|
|
|
// Convert coord
|
|
f32 x = _x * _gridSample + (_gridSample * 0.5f) + m_obb.getMin().x();
|
|
f32 y = _y * _gridSample + (_gridSample * 0.5f) + m_obb.getMin().y();
|
|
|
|
Vec2d randPointA(0.0f, y);
|
|
Vec2d randPointB(x, y);
|
|
|
|
Vec2d res;
|
|
u32 countIntersec = 0;
|
|
|
|
for (u32 j = idxBegin; j < idxEnd; j++)
|
|
{
|
|
Vec2d& pointA = m_points[j];
|
|
Vec2d& pointB = m_points[j+1];
|
|
|
|
if (Segment_Segment(randPointA, randPointB, pointA, pointB, res, 0.0f))
|
|
countIntersec ++;
|
|
}
|
|
|
|
// last segment
|
|
Vec2d& pointA = m_points[idxEnd];
|
|
Vec2d& pointB = m_points[idxBegin];
|
|
|
|
if (Segment_Segment(randPointA, randPointB, pointA, pointB, res, 0.0f))
|
|
countIntersec ++;
|
|
|
|
return (countIntersec % 2) != 0;
|
|
}
|
|
|
|
void PackingPatch::sample(f32 _gridSample)
|
|
{
|
|
// Init sampling
|
|
m_widthSampling = (u32)ceil((f32)m_obb.getWidth() / _gridSample);
|
|
m_heightSampling = (u32)ceil((f32)m_obb.getHeight() / _gridSample);
|
|
|
|
m_sampling.resize(m_widthSampling * m_heightSampling);
|
|
memset(&m_sampling[0], 0, m_widthSampling * m_heightSampling *sizeof(u32));
|
|
|
|
SafeArray<u32> tmpSampling;
|
|
u32 tmpWidthSampling = m_widthSampling+2;
|
|
u32 tmpHeightSampling = m_heightSampling+2;
|
|
|
|
tmpSampling.resize(tmpWidthSampling*tmpHeightSampling);
|
|
|
|
const u32 countPoly = m_subPoly.size();
|
|
for (u32 polyIdx = 0; polyIdx < countPoly; polyIdx++)
|
|
{
|
|
u32 idxBegin = m_subPoly[polyIdx].m_begin;
|
|
u32 idxEnd = m_subPoly[polyIdx].m_end;
|
|
u32 fillIdx = 2*polyIdx+1;
|
|
if (idxEnd > idxBegin)
|
|
{
|
|
// Compute border
|
|
for (u32 j = idxBegin; j < idxEnd; j++)
|
|
{
|
|
Vec2d& pointA = m_points[j];
|
|
Vec2d& pointB = m_points[j+1];
|
|
|
|
gridFill(_gridSample, pointA, pointB, fillIdx);
|
|
}
|
|
|
|
Vec2d& pointA = m_points[idxEnd];
|
|
Vec2d& pointB = m_points[idxBegin];
|
|
|
|
gridFill(_gridSample, pointA, pointB, fillIdx);
|
|
|
|
memset(&tmpSampling[0], 0, tmpWidthSampling*tmpHeightSampling*sizeof(u32));
|
|
for (u32 yIdx=0; yIdx<m_heightSampling; yIdx++)
|
|
{
|
|
for (u32 xIdx=0; xIdx<m_widthSampling; xIdx++)
|
|
{
|
|
if ((m_sampling[xIdx + yIdx*m_widthSampling] & SAMPLE_MASK) == fillIdx)
|
|
tmpSampling[xIdx + 1 + (yIdx + 1)*tmpWidthSampling] = 1;
|
|
}
|
|
}
|
|
seedFill(tmpSampling, 0, 0, tmpWidthSampling, tmpHeightSampling, 1);
|
|
for (u32 yIdx=0; yIdx<m_heightSampling; yIdx++)
|
|
{
|
|
for (u32 xIdx=0; xIdx<m_widthSampling; xIdx++)
|
|
{
|
|
if (!tmpSampling[xIdx + 1 + (yIdx + 1)*tmpWidthSampling])
|
|
m_sampling[xIdx + yIdx*m_widthSampling] = fillIdx+1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//*****************************************************************************
|
|
|
|
Vec2d PackingPatch::getBaryCenter()
|
|
{
|
|
const u32 countPoints = m_points.size();
|
|
|
|
Vec2d baryCenter = Vec2d::Zero;
|
|
|
|
for (u32 j = 0; j < countPoints; j++)
|
|
{
|
|
Vec2d& point = m_points[j];
|
|
|
|
baryCenter += point;
|
|
|
|
}
|
|
|
|
if (countPoints > 0)
|
|
{
|
|
baryCenter = baryCenter / (f32)countPoints;
|
|
}
|
|
|
|
return baryCenter;
|
|
|
|
}
|
|
//*****************************************************************************
|
|
|
|
//*****************************************************************************
|
|
|
|
void PackingPatch::getCoorSample(f32 _gridSample, const Vec2d & _pos, i32& _x, i32& _y)
|
|
{
|
|
_x = (i32)((_pos.x() - m_obb.getMin().x()) / _gridSample);
|
|
_y = (i32)((_pos.y() - m_obb.getMin().y()) / _gridSample);
|
|
|
|
_x = Clamp(_x, 0, (i32)(m_widthSampling - 1));
|
|
_y = Clamp(_y, 0, (i32)(m_heightSampling - 1));
|
|
|
|
}
|
|
|
|
//*****************************************************************************
|
|
void PackingPatch::fillPoint(i32 x, i32 y, u32 idx)
|
|
{
|
|
//(0: vide, 1: rempli)
|
|
|
|
//masque.m_masque[x][y] = 1;
|
|
u32 index = y * m_widthSampling + x;
|
|
ITF_ASSERT(index < m_widthSampling * m_heightSampling);
|
|
u32 mask = m_sampling[index] & SAMPLE_TAG;
|
|
if (m_sampling[index] != 0 && ((m_sampling[index] & SAMPLE_MASK) % 2) == 0)
|
|
mask |= SAMPLE_TAG;
|
|
m_sampling[index] = idx | mask;
|
|
}
|
|
|
|
void PackingPatch::bresenham(i32 x1, i32 y1, i32 x2, i32 y2, u32 idx)
|
|
{
|
|
const u32 maxSample = m_widthSampling * m_heightSampling;
|
|
|
|
i32 xa = x1;
|
|
i32 xb = x2;
|
|
i32 ya = y1;
|
|
i32 yb = y2;
|
|
|
|
int x,y,dx,dy,incrmX,incrmY,dp,NE,SE;
|
|
|
|
dx = xb-xa;
|
|
dy = yb-ya;
|
|
|
|
if (dx>0) {
|
|
incrmX = 1;
|
|
} else {
|
|
incrmX = -1;
|
|
dx *= -1;
|
|
}
|
|
|
|
if (dy>0){
|
|
incrmY = 1;
|
|
} else {
|
|
incrmY = -1;
|
|
dy *= -1;
|
|
}
|
|
|
|
if (dx>=dy) {
|
|
dp=2*dy-dx;
|
|
SE=2*dy;
|
|
NE=2*(dy-dx);
|
|
|
|
y=ya;
|
|
|
|
for(x=xa;x!=xb;x+=incrmX) {
|
|
fillPoint(x, y, idx);
|
|
if (dp<=0) { /* on choisit le pixel E */
|
|
dp += SE;
|
|
} else {
|
|
dp += NE;
|
|
y+=incrmY;
|
|
}
|
|
}
|
|
} else if (dx<dy) {
|
|
dp=2*dx-dy;
|
|
SE=2*dx;
|
|
NE=2*(dx-dy);
|
|
|
|
x=xa;
|
|
for(y=ya;y!=yb;y+=incrmY) {
|
|
fillPoint(x, y, idx);
|
|
|
|
if (dp<=0) { /* on choisit le pixel E */
|
|
dp += SE;
|
|
} else {
|
|
dp += NE;
|
|
x+=incrmX;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void PackingPatch::andres(i32 x1, i32 y1, i32 x2, i32 y2, u32 idx)
|
|
{
|
|
int i; // loop counter
|
|
int ystep, xstep; // the step on y and x axis
|
|
int error; // the error accumulated during the increment
|
|
int errorprev; // *vision the previous value of the error variable
|
|
int y = y1, x = x1; // the line points
|
|
int ddy, ddx; // compulsory variables: the double values of dy and dx
|
|
int dx = x2 - x1;
|
|
int dy = y2 - y1;
|
|
|
|
if (dy < 0)
|
|
{
|
|
ystep = -1;
|
|
dy = -dy;
|
|
}
|
|
else
|
|
{
|
|
ystep = 1;
|
|
}
|
|
|
|
if (dx < 0)
|
|
{
|
|
xstep = -1;
|
|
dx = -dx;
|
|
}
|
|
else
|
|
{
|
|
xstep = 1;
|
|
}
|
|
|
|
ddy = 2 * dy;
|
|
ddx = 2 * dx;
|
|
|
|
if (ddx >= ddy)
|
|
{
|
|
errorprev = error = dx;
|
|
for (i=0 ; i < dx ; i++)
|
|
{
|
|
x += xstep;
|
|
error += ddy;
|
|
if (error > ddx)
|
|
{
|
|
y += ystep;
|
|
error -= ddx;
|
|
|
|
if (error + errorprev < ddx)
|
|
{
|
|
fillPoint(x, y-ystep, idx);
|
|
}
|
|
else if (error + errorprev > ddx)
|
|
{
|
|
fillPoint(x-xstep, y, idx);
|
|
}
|
|
else
|
|
{
|
|
fillPoint(x-xstep, y, idx);
|
|
fillPoint(x, y-ystep, idx);
|
|
}
|
|
}
|
|
fillPoint(x, y, idx);
|
|
errorprev = error;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorprev = error = dy;
|
|
for (i=0 ; i < dy ; i++)
|
|
{
|
|
y += ystep;
|
|
error += ddx;
|
|
if (error > ddy)
|
|
{
|
|
x += xstep;
|
|
error -= ddy;
|
|
if (error + errorprev < ddy)
|
|
{
|
|
fillPoint(x-xstep, y, idx);
|
|
}
|
|
else if (error + errorprev > ddy)
|
|
{
|
|
fillPoint(x, y-ystep, idx);
|
|
}
|
|
else
|
|
{
|
|
fillPoint(x-xstep, y, idx);
|
|
fillPoint(x, y-ystep, idx);
|
|
}
|
|
}
|
|
fillPoint(x, y, idx);
|
|
errorprev = error;
|
|
}
|
|
}
|
|
}
|
|
|
|
bbool Segment_Segment_Collinear(const Vec2d & _p1, const Vec2d & _p2, const Vec2d & _p3, const Vec2d & _p4)
|
|
{
|
|
// are vectors //
|
|
if (f32_Abs((_p2-_p1).cross(_p4-_p3)) > MTH_EPSILON)
|
|
return bfalse;
|
|
|
|
// are points collinear
|
|
if (f32_Abs((_p2-_p1).cross(_p4-_p1)) > MTH_EPSILON)
|
|
return bfalse;
|
|
|
|
// do point have common part
|
|
if ((_p3-_p1).dot(_p3-_p2) <= 0.f)
|
|
return btrue;
|
|
if ((_p4-_p1).dot(_p4-_p2) <= 0.f)
|
|
return btrue;
|
|
if ((_p1-_p3).dot(_p1-_p4) <= 0.f)
|
|
return btrue;
|
|
if ((_p2-_p3).dot(_p1-_p4) <= 0.f)
|
|
return btrue;
|
|
|
|
return bfalse;
|
|
}
|
|
|
|
void PackingPatch::gridFill( f32 _gridSample, Vec2d &_p1, Vec2d &_p2, u32 _idx )
|
|
{
|
|
i32 x1, x2, y1, y2;
|
|
getCoorSample(_gridSample, _p1, x1, y1);
|
|
getCoorSample(_gridSample, _p2, x2, y2);
|
|
if (x1 > x2)
|
|
{
|
|
i32 swap = x2;
|
|
x2 = x1;
|
|
x1 = swap;
|
|
}
|
|
if (y1 > y2)
|
|
{
|
|
i32 swap = y2;
|
|
y2 = y1;
|
|
y1 = swap;
|
|
}
|
|
|
|
Vec2d res;
|
|
Vec2d quad[4];
|
|
for (i32 y=y1; y<=y2; y++)
|
|
{
|
|
for (i32 x=x1; x<=x2; x++)
|
|
{
|
|
// check if x y cuts the line
|
|
quad[0] = Vec2d((f32)x, (f32)y ) * _gridSample + m_obb.getMin();
|
|
quad[1] = Vec2d((f32)x+1, (f32)y ) * _gridSample + m_obb.getMin();
|
|
quad[2] = Vec2d((f32)x, (f32)y+1) * _gridSample + m_obb.getMin();
|
|
quad[3] = Vec2d((f32)x+1, (f32)y+1) * _gridSample + m_obb.getMin();
|
|
|
|
bbool intersect = bfalse;
|
|
if (!intersect && (Segment_Segment(quad[0], quad[1], _p1, _p2, res, 0.0f) || Segment_Segment_Collinear(quad[0], quad[1], _p1, _p2)))
|
|
intersect = btrue;
|
|
if (!intersect && (Segment_Segment(quad[0], quad[2], _p1, _p2, res, 0.0f) || Segment_Segment_Collinear(quad[0], quad[2], _p1, _p2)))
|
|
intersect = btrue;
|
|
if (!intersect && (Segment_Segment(quad[3], quad[1], _p1, _p2, res, 0.0f) || Segment_Segment_Collinear(quad[3], quad[1], _p1, _p2)))
|
|
intersect = btrue;
|
|
if (!intersect && (Segment_Segment(quad[3], quad[2], _p1, _p2, res, 0.0f) || Segment_Segment_Collinear(quad[3], quad[2], _p1, _p2)))
|
|
intersect = btrue;
|
|
|
|
if (intersect)
|
|
fillPoint(x, y, _idx);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
} |