forked from 0ad/0ad
304 lines
9.0 KiB
C++
304 lines
9.0 KiB
C++
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// Name: AlphaMapCalculator.cpp
|
||
|
// Author: Rich Cross
|
||
|
// Contact: rich@wildfiregames.com
|
||
|
//
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#include "AlphaMapCalculator.h"
|
||
|
#include <string.h>
|
||
|
#include <stdio.h>
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
// CAlphaMapCalculator: functionality for calculating which alpha blend map
|
||
|
// fits a given shape
|
||
|
namespace CAlphaMapCalculator {
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
// Blend4: structure mapping a blend shape for N,E,S,W to a particular map
|
||
|
struct Blend4 {
|
||
|
Blend4(BlendShape4 shape,int alphamap) : m_Shape(shape), m_AlphaMap(alphamap) {}
|
||
|
|
||
|
BlendShape4 m_Shape;
|
||
|
int m_AlphaMap;
|
||
|
};
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
// Blend8: structure mapping a blend shape for N,NE,E,SE,S,SW,W,NW to a
|
||
|
// particular map
|
||
|
struct Blend8 {
|
||
|
Blend8(BlendShape8 shape,int alphamap) : m_Shape(shape), m_AlphaMap(alphamap) {}
|
||
|
|
||
|
BlendShape8 m_Shape;
|
||
|
int m_AlphaMap;
|
||
|
};
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
// Data tables for mapping between shapes and blend maps
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
const Blend4 Blends1Neighbour[] =
|
||
|
{
|
||
|
Blend4(BlendShape4(1,0,0,0), 12)
|
||
|
};
|
||
|
|
||
|
|
||
|
const Blend4 Blends2Neighbour[] =
|
||
|
{
|
||
|
Blend4(BlendShape4(0,1,1,0), 7),
|
||
|
Blend4(BlendShape4(1,0,1,0), 10)
|
||
|
};
|
||
|
|
||
|
const Blend8 Blends2Neighbour8[] =
|
||
|
{
|
||
|
Blend8(BlendShape8(1,1,0,0,0,0,0,0), 12),
|
||
|
Blend8(BlendShape8(1,0,0,0,0,1,0,0), 12),
|
||
|
Blend8(BlendShape8(0,1,0,1,0,0,0,0), 0) ,
|
||
|
Blend8(BlendShape8(0,1,0,0,0,1,0,0), 0)
|
||
|
};
|
||
|
|
||
|
const Blend4 Blends3Neighbour[] =
|
||
|
{
|
||
|
Blend4(BlendShape4(1,1,1,0), 4)
|
||
|
};
|
||
|
|
||
|
const Blend8 Blends3Neighbour8[] =
|
||
|
{
|
||
|
Blend8(BlendShape8(1,1,0,0,1,0,0,0), 10),
|
||
|
Blend8(BlendShape8(1,1,0,0,0,0,0,1), 12),
|
||
|
Blend8(BlendShape8(1,1,1,0,0,0,0,0), 1),
|
||
|
Blend8(BlendShape8(0,1,1,0,1,0,0,0), 7),
|
||
|
Blend8(BlendShape8(0,0,1,0,1,0,1,0), 4),
|
||
|
Blend8(BlendShape8(1,1,0,0,0,1,0,0), 12),
|
||
|
Blend8(BlendShape8(1,1,0,1,0,0,0,0), 12),
|
||
|
Blend8(BlendShape8(0,0,1,0,1,0,0,1), 7),
|
||
|
Blend8(BlendShape8(1,0,0,1,0,1,0,0), 12),
|
||
|
Blend8(BlendShape8(0,1,0,1,0,1,0,0), 0)
|
||
|
};
|
||
|
|
||
|
const Blend8 Blends4Neighbour8[] =
|
||
|
{
|
||
|
Blend8(BlendShape8(1,1,0,0,1,0,0,1), 10),
|
||
|
Blend8(BlendShape8(1,1,0,1,1,0,0,0), 10),
|
||
|
Blend8(BlendShape8(1,1,0,0,1,1,0,0), 10),
|
||
|
Blend8(BlendShape8(1,1,0,1,0,0,0,1), 12),
|
||
|
Blend8(BlendShape8(0,1,1,0,1,1,0,0), 7),
|
||
|
Blend8(BlendShape8(1,1,1,1,0,0,0,0), 1),
|
||
|
Blend8(BlendShape8(1,1,1,0,1,0,0,0), 3),
|
||
|
Blend8(BlendShape8(0,0,1,0,1,1,0,1), 7),
|
||
|
Blend8(BlendShape8(1,0,1,0,1,1,0,0), 4),
|
||
|
Blend8(BlendShape8(1,1,1,0,0,1,0,0), 1),
|
||
|
Blend8(BlendShape8(1,1,0,1,0,1,0,0), 12),
|
||
|
Blend8(BlendShape8(0,1,0,1,0,1,0,1), 0)
|
||
|
};
|
||
|
|
||
|
const Blend8 Blends5Neighbour8[] =
|
||
|
{
|
||
|
Blend8(BlendShape8(1,1,1,1,1,0,0,0), 2),
|
||
|
Blend8(BlendShape8(1,1,1,1,0,0,0,1), 1),
|
||
|
Blend8(BlendShape8(1,1,1,0,1,0,0,1), 3),
|
||
|
Blend8(BlendShape8(1,1,1,0,1,0,1,0), 11),
|
||
|
Blend8(BlendShape8(1,1,1,0,0,1,0,1), 1),
|
||
|
Blend8(BlendShape8(1,1,0,1,1,1,0,0), 10),
|
||
|
Blend8(BlendShape8(1,1,1,0,1,1,0,0), 3),
|
||
|
Blend8(BlendShape8(1,0,1,0,1,1,0,1), 4),
|
||
|
Blend8(BlendShape8(1,1,0,1,0,1,0,1), 12),
|
||
|
Blend8(BlendShape8(0,1,1,0,1,1,0,1), 7)
|
||
|
};
|
||
|
|
||
|
const Blend8 Blends6Neighbour8[] =
|
||
|
{
|
||
|
Blend8(BlendShape8(1,1,1,1,1,1,0,0), 2),
|
||
|
Blend8(BlendShape8(1,1,1,1,1,0,1,0), 8),
|
||
|
Blend8(BlendShape8(1,1,1,1,0,1,0,1), 1),
|
||
|
Blend8(BlendShape8(1,1,1,0,1,1,1,0), 6),
|
||
|
Blend8(BlendShape8(1,1,1,0,1,1,0,1), 3),
|
||
|
Blend8(BlendShape8(1,1,0,1,1,1,0,1), 10)
|
||
|
};
|
||
|
|
||
|
const Blend8 Blends7Neighbour8[] =
|
||
|
{
|
||
|
Blend8(BlendShape8(1,1,1,1,1,1,0,1), 2),
|
||
|
Blend8(BlendShape8(1,1,1,1,1,1,1,0), 9)
|
||
|
};
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
// MatchBlendShapeFlipped: test if the given shape can be made to fit the
|
||
|
// template in either unflipped state, or by flipping the shape in U or V
|
||
|
template<class T>
|
||
|
bool MatchBlendShapeFlipped(const T& templateshape,const T& shape,unsigned int& flags)
|
||
|
{
|
||
|
// test unrotated shape
|
||
|
if (shape==templateshape) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// test against shape flipped in U
|
||
|
T tstShape;
|
||
|
templateshape.FlipU(tstShape);
|
||
|
if (shape==tstShape) {
|
||
|
flags|=BLENDMAP_FLIPU;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// test against shape flipped in V
|
||
|
templateshape.FlipV(tstShape);
|
||
|
if (shape==tstShape) {
|
||
|
flags|=BLENDMAP_FLIPV;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// no joy; no match by flipping
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
// MatchBlendShape: try and find a matching blendmap, and the required flip/
|
||
|
// rotation flags, to fit the given shape to the template
|
||
|
template<class T>
|
||
|
int MatchBlendShape(const T& templateshape,const T& shape,unsigned int& flags)
|
||
|
{
|
||
|
// try matching unrotated shape first using just flipping
|
||
|
if (MatchBlendShapeFlipped(templateshape,shape,flags)) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// now try iterating through rotations of 90,180,270 degrees
|
||
|
T tstShape;
|
||
|
templateshape.Rotate90(tstShape);
|
||
|
if (MatchBlendShapeFlipped(tstShape,shape,flags)) {
|
||
|
// update flags - note if we've flipped in u or v, we need to rotate in
|
||
|
// the opposite direction
|
||
|
flags|=flags ? BLENDMAP_ROTATE270 : BLENDMAP_ROTATE90;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
templateshape.Rotate180(tstShape);
|
||
|
if (MatchBlendShapeFlipped(tstShape,shape,flags)) {
|
||
|
flags|=BLENDMAP_ROTATE180;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
templateshape.Rotate270(tstShape);
|
||
|
if (MatchBlendShapeFlipped(tstShape,shape,flags)) {
|
||
|
// update flags - note if we've flipped in u or v, we need to rotate in
|
||
|
// the opposite direction
|
||
|
flags|=flags ? BLENDMAP_ROTATE90 : BLENDMAP_ROTATE270;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
// LookupBlend: find and return the blendmap fitting the given shape by
|
||
|
// iterating through the given data table and testing each shape in flipped and
|
||
|
// rotated forms until a match is found
|
||
|
template<class S,class T>
|
||
|
int LookupBlend(int tableSize,const S* table,const T& shape,unsigned int& flags)
|
||
|
{
|
||
|
// iterate through known blend shapes
|
||
|
for (int b=0;b<tableSize;b++) {
|
||
|
const S& blend=table[b];
|
||
|
if (MatchBlendShape(blend.m_Shape,shape,flags)) {
|
||
|
return blend.m_AlphaMap;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// eh? shouldn't get here if we've correctly considered all possible cases;
|
||
|
// keep the compiler happy, and, while we're still debugging possible shapes,
|
||
|
// return bad blend to highlight suspect alphamap logic
|
||
|
return 13;
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
// Calculate: return the index of the blend map that fits the given shape,
|
||
|
// and the set of flip/rotation flags to get the shape correctly oriented
|
||
|
int Calculate(BlendShape8 shape,unsigned int& flags)
|
||
|
{
|
||
|
// assume we're not going to require flipping or rotating
|
||
|
flags=0;
|
||
|
|
||
|
// count number of neighbours
|
||
|
int count=0;
|
||
|
for (int i=0;i<8;i++) {
|
||
|
if (shape[i]) count++;
|
||
|
}
|
||
|
|
||
|
if (count==0) {
|
||
|
// no neighbours, just the centre tile has the given texture; use blend circle
|
||
|
return 0;
|
||
|
} else if (count==8) {
|
||
|
// all neighbours have same texture; return code to signal no alphamap required
|
||
|
return -1;
|
||
|
} else {
|
||
|
if (count<=4) {
|
||
|
// check if we can consider this a BlendShape4 - ie are any of the diagonals (NE,SE,SW,NW) set?
|
||
|
if (!shape[1] && !shape[3] && !shape[5] && !shape[7]) {
|
||
|
// ok, build a BlendShape4 and use that
|
||
|
BlendShape4 shape4;
|
||
|
shape4[0]=shape[0];
|
||
|
shape4[1]=shape[2];
|
||
|
shape4[2]=shape[4];
|
||
|
shape4[3]=shape[6];
|
||
|
|
||
|
switch (count) {
|
||
|
case 1:
|
||
|
return LookupBlend(sizeof(Blends1Neighbour)/sizeof(Blend4),Blends1Neighbour,shape4,flags);
|
||
|
|
||
|
case 2:
|
||
|
return LookupBlend(sizeof(Blends2Neighbour)/sizeof(Blend4),Blends2Neighbour,shape4,flags);
|
||
|
|
||
|
case 3:
|
||
|
return LookupBlend(sizeof(Blends3Neighbour)/sizeof(Blend4),Blends3Neighbour,shape4,flags);
|
||
|
|
||
|
case 4:
|
||
|
// N,S,E,W have same texture, NE,SE,SW,NW don't; use a blend 4 corners
|
||
|
return 5;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// we've got this far, so now we've got to consider the remaining choices, all containing
|
||
|
// diagonal elements
|
||
|
switch (count) {
|
||
|
case 1:
|
||
|
// trivial case - just return a circle blend
|
||
|
return 0;
|
||
|
|
||
|
case 2:
|
||
|
return LookupBlend(sizeof(Blends2Neighbour8)/sizeof(Blend8),Blends2Neighbour8,shape,flags);
|
||
|
|
||
|
case 3:
|
||
|
return LookupBlend(sizeof(Blends3Neighbour8)/sizeof(Blend8),Blends3Neighbour8,shape,flags);
|
||
|
|
||
|
case 4:
|
||
|
return LookupBlend(sizeof(Blends4Neighbour8)/sizeof(Blend8),Blends4Neighbour8,shape,flags);
|
||
|
|
||
|
case 5:
|
||
|
return LookupBlend(sizeof(Blends5Neighbour8)/sizeof(Blend8),Blends5Neighbour8,shape,flags);
|
||
|
|
||
|
case 6:
|
||
|
return LookupBlend(sizeof(Blends6Neighbour8)/sizeof(Blend8),Blends6Neighbour8,shape,flags);
|
||
|
|
||
|
case 7:
|
||
|
return LookupBlend(sizeof(Blends7Neighbour8)/sizeof(Blend8),Blends7Neighbour8,shape,flags);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// Shouldn't get here if we've correctly considered all possible cases;
|
||
|
// keep the compiler happy, and, while we're still debugging possible shapes,
|
||
|
// return bad blend to highlight suspect alphamap logic
|
||
|
return 13;
|
||
|
}
|
||
|
|
||
|
} // end of namespace
|