/** * ========================================================================= * File : AlphaMapCalculator.cp * Project : 0 A.D. * Description : Determine which alpha blend map fits a given shape. * ========================================================================= */ #include "precompiled.h" #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