//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//
//=============================================================================//
//-----------------------------------------------------------------------------
// FILE: TRISTRIP.CPP
//
// Desc: Xbox tristripper
//
// Copyright (c) 1999-2000 Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------

// identifier was truncated to '255' characters in the debug information
#pragma warning(disable: 4786)
// conversion from 'double' to 'float'
#pragma warning(disable: 4244)
#pragma warning(disable: 4530)

#include <stdio.h>
#include <stdarg.h>
#include <algorithm>
#include <list>
#include <vector>

#include <assert.h>
#ifdef _DEBUG
#include <crtdbg.h>
#endif

#include "mstristrip.h"

using namespace std;

//=========================================================================
// structs
//=========================================================================
typedef vector<WORD> STRIPVERTS;
typedef list<STRIPVERTS *> STRIPLIST;
typedef WORD (*TRIANGLELIST)[3];

struct TRIANGLEINFO
{
    int neighbortri[3];
    int neighboredge[3];
};

// return true if strip starts clockwise
inline bool FIsStripCW(const STRIPVERTS &stripvertices)
{
    // last index should have cw/ccw bool
    return !!stripvertices[stripvertices.size() - 1];
}

// return length of strip
inline int StripLen(const STRIPVERTS &stripvertices)
{
    return (int)stripvertices.size() - 1;
}

// free all stripverts and clear the striplist
inline void FreeStripListVerts(STRIPLIST *pstriplist)
{
    STRIPLIST::iterator istriplist = pstriplist->begin();
    while(istriplist != pstriplist->end())
    {
        STRIPVERTS *pstripverts = *istriplist;
        delete pstripverts;
        pstriplist->erase(istriplist++);
    }
}

//=========================================================================
// main stripper class
//=========================================================================
class CStripper
{
public:
    // ctors/dtors
    CStripper(int numtris, TRIANGLELIST ptriangles);
    ~CStripper();

    // initialize tri info
    void InitTriangleInfo(int tri, int vert);

    // get maximum length strip from tri/vert
    int CreateStrip(int tri, int vert, int maxlen, int *pswaps,
        bool flookahead, bool fstartcw, int *pstriptris, int *pstripverts);

    // stripify entire mesh
    void BuildStrips(STRIPLIST *pstriplist, int maxlen, bool flookahead);

    // blast strip indices to ppstripindices
    int CreateManyStrips(STRIPLIST *pstriplist, WORD **ppstripindices);
    int CreateLongStrip(STRIPLIST *pstriplist, WORD **ppstripindices);

    inline int GetNeighborCount(int tri)
    {
        int count = 0;
        for(int vert = 0; vert < 3; vert++)
        {
            int neighbortri = m_ptriinfo[tri].neighbortri[vert];
            count += (neighbortri != -1) && !m_pused[neighbortri];
        }
        return count;
    }

    // from callee
    int m_numtris;              // # tris
    TRIANGLELIST m_ptriangles;  // trilist

    TRIANGLEINFO *m_ptriinfo;   // tri edge, neighbor info
    int *m_pused;               // tri used flag
};

//=========================================================================
// vertex cache class
//=========================================================================
class CVertCache
{
public:
    CVertCache()
        { Reset(); }
    ~CVertCache()
        {};

    // reset cache
    void Reset()
    {
        m_iCachePtr = 0;
        m_cachehits = 0;
        memset(m_rgCache, 0xff, sizeof(m_rgCache));
    }

    // add vertindex to cache
    bool Add(int strip, int vertindex);

    int NumCacheHits() const
        { return m_cachehits; }

//    enum { CACHE_SIZE = 10 };
    enum { CACHE_SIZE = 18 };

private:
    int m_cachehits;                // current # of cache hits
    WORD m_rgCache[CACHE_SIZE];     // vertex cache
    int m_rgCacheStrip[CACHE_SIZE]; // strip # which added vert
    int m_iCachePtr;                // fifo ptr
};

//=========================================================================
// Get maximum length of strip starting at tri/vert
//=========================================================================
int CStripper::CreateStrip(int tri, int vert, int maxlen, int *pswaps,
    bool flookahead, bool fstartcw, int *pstriptris, int *pstripverts)
{
    *pswaps = 0;

    // this guy has already been used?
    if(m_pused[tri])
        return 0;

    // mark tri as used
    m_pused[tri] = 1;

    int swaps = 0;

    // add first tri info
    pstriptris[0] = tri;
    pstriptris[1] = tri;
    pstriptris[2] = tri;

    if(fstartcw)
    {
        pstripverts[0] = (vert) % 3;
        pstripverts[1] = (vert + 1) % 3;
        pstripverts[2] = (vert + 2) % 3;
    }
    else
    {
        pstripverts[0] = (vert + 1) % 3;
        pstripverts[1] = (vert + 0) % 3;
        pstripverts[2] = (vert + 2) % 3;
    }
    fstartcw = !fstartcw;

    // get next tri information
    int edge = (fstartcw ? vert + 2 : vert + 1) % 3;
    int nexttri = m_ptriinfo[tri].neighbortri[edge];
    int nextvert = m_ptriinfo[tri].neighboredge[edge];

    // start building the strip until we run out of room or indices
	int stripcount;
    for( stripcount = 3; stripcount < maxlen; stripcount++)
    {
        // dead end?
        if(nexttri == -1 || m_pused[nexttri])
            break;

        // move to next tri
        tri = nexttri;
        vert = nextvert;

        // toggle orientation
        fstartcw = !fstartcw;

        // find the next natural edge
        int edge = (fstartcw ? vert + 2 : vert + 1) % 3;
        nexttri = m_ptriinfo[tri].neighbortri[edge];
        nextvert = m_ptriinfo[tri].neighboredge[edge];

        bool fswap = false;
        if(nexttri == -1 || m_pused[nexttri])
        {
            // if the next tri is a dead end - try swapping orientation
            fswap = true;
        }
        else if(flookahead)
        {
            // try a swap and see who our new neighbor would be
            int edgeswap = (fstartcw ? vert + 1 : vert + 2) % 3;
            int nexttriswap = m_ptriinfo[tri].neighbortri[edgeswap];
            int nextvertswap = m_ptriinfo[tri].neighboredge[edgeswap];

            if(nexttriswap != -1 && !m_pused[nexttriswap])
            {
                assert(nexttri != -1);

                // if the swap neighbor has a lower count, change directions
                if(GetNeighborCount(nexttriswap) < GetNeighborCount(nexttri))
                {
                    fswap = true;
                }
                else if(GetNeighborCount(nexttriswap) == GetNeighborCount(nexttri))
                {
                    // if they have the same number of neighbors - check their neighbors
                    edgeswap = (fstartcw ? nextvertswap + 2 : nextvertswap + 1) % 3;
                    nexttriswap = m_ptriinfo[nexttriswap].neighbortri[edgeswap];

                    int edge1 = (fstartcw ? nextvert + 1 : nextvert + 2) % 3;
                    int nexttri1 = m_ptriinfo[nexttri].neighbortri[edge1];

                    if(nexttri1 == -1 || m_pused[nexttri1])
                    {
                        // natural winding order leads us to a dead end so turn
                        fswap = true;
                    }
                    else if(nexttriswap != -1 && !m_pused[nexttriswap])
                    {
                        // check neighbor counts on both directions and swap if it's better
                        if(GetNeighborCount(nexttriswap) < GetNeighborCount(nexttri1))
                            fswap = true;
                    }
                }
            }
        }

        if(fswap)
        {
            // we've been told to change directions so make sure we actually can
            // and then add the swap vertex
            int edgeswap = (fstartcw ? vert + 1 : vert + 2) % 3;
            nexttri = m_ptriinfo[tri].neighbortri[edgeswap];
            nextvert = m_ptriinfo[tri].neighboredge[edgeswap];

            if(nexttri != -1 && !m_pused[nexttri])
            {
                pstriptris[stripcount] = pstriptris[stripcount - 2];
                pstripverts[stripcount] = pstripverts[stripcount - 2];
                stripcount++;
                swaps++;
                fstartcw = !fstartcw;
            }
        }

        // record index information
        pstriptris[stripcount] = tri;
        pstripverts[stripcount] = (vert + 2) % 3;

        // mark triangle as used
        m_pused[tri] = 1;
    }

    // clear the used flags
    for(int j = 2; j < stripcount; j++)
        m_pused[pstriptris[j]] = 0;

    // return swap count and striplen
    *pswaps = swaps;
    return stripcount;
}

//=========================================================================
// Given a striplist and current cache state, pick the best next strip
//=========================================================================
STRIPLIST::iterator FindBestCachedStrip(STRIPLIST *pstriplist,
    const CVertCache &vertcachestate)
{
    if(pstriplist->empty())
		return pstriplist->end();

    bool fFlipStrip = false;
    int maxcachehits = -1;
    STRIPLIST::iterator istriplistbest = pstriplist->begin();

    int striplen = StripLen(**istriplistbest);
    bool fstartcw = FIsStripCW(**istriplistbest);

    // go through all the other strips looking for the best caching
    for(STRIPLIST::iterator istriplist = pstriplist->begin();
        istriplist != pstriplist->end();
        ++istriplist)
    {
        bool fFlip = false;
        const STRIPVERTS &stripverts = **istriplist;
        int striplennew = StripLen(stripverts);

        // check cache if this strip is the same type as us (ie: cw/odd)
        if((FIsStripCW(stripverts) == fstartcw) &&
            ((striplen & 0x1) == (striplennew & 0x1)))
        {
            // copy current state of cache
            CVertCache vertcachenew = vertcachestate;

            // figure out what this guy would do to our cache
            for(int ivert = 0; ivert < striplennew; ivert++)
                vertcachenew.Add(2, stripverts[ivert]);

            // even length strip - see if better cache hits reversed
            if(!(striplennew & 0x1))
            {
                CVertCache vertcacheflipped = vertcachestate;

                for(int ivert = StripLen(stripverts) - 1; ivert >= 0; ivert--)
                    vertcacheflipped.Add(2, stripverts[ivert]);

                if(vertcacheflipped.NumCacheHits() > vertcachenew.NumCacheHits())
                {
                    vertcachenew = vertcacheflipped;
                    fFlip = true;
                }
            }

            // record the best number of cache hits to date
            int numcachehits = vertcachenew.NumCacheHits() - vertcachestate.NumCacheHits();
            if(numcachehits > maxcachehits)
            {
                maxcachehits = numcachehits;
                istriplistbest = istriplist;
                fFlipStrip = fFlip;
            }
        }
    }

    if(fFlipStrip)
    {
        STRIPVERTS &stripverts = **istriplistbest;
        STRIPVERTS::iterator vend = stripverts.end();
        
        reverse(stripverts.begin(), --vend);
    }

    // make sure we keep the list in order and always pull off
    // the first dude.
    if(istriplistbest != pstriplist->begin())
        swap(*istriplistbest, *pstriplist->begin());

    return pstriplist->begin();
}


//=========================================================================
// Don't merge the strips - just blast em into the stripbuffer one by one
// (useful for debugging)
//=========================================================================
int CStripper::CreateManyStrips(STRIPLIST *pstriplist, WORD **ppstripindices)
{
    // allow room for each of the strips size plus the final 0
    int indexcount = (int)pstriplist->size() + 1;

    // we're storing the strips in [size1 i1 i2 i3][size2 i4 i5 i6][0] format
	STRIPLIST::iterator istriplist;
    for( istriplist = pstriplist->begin(); istriplist != pstriplist->end(); ++istriplist)
    {
        // add striplength plus potential degenerate to swap ccw --> cw
        indexcount += StripLen(**istriplist) + 1;
    }

    // alloc the space for all this stuff
    WORD *pstripindices = new WORD [indexcount];
    assert(pstripindices);

    CVertCache vertcache;
    int numstripindices = 0;

    for(istriplist = pstriplist->begin();
        !pstriplist->empty();
        istriplist = FindBestCachedStrip(pstriplist, vertcache))
    {
        const STRIPVERTS &stripverts = **istriplist;

        if(!FIsStripCW(stripverts))
        {
            // add an extra index if it's ccw
            pstripindices[numstripindices++] = StripLen(stripverts) + 1;
            pstripindices[numstripindices++] = stripverts[0];
        }
        else
        {
            // add the strip length
            pstripindices[numstripindices++] = StripLen(stripverts);
        }

        // add all the strip indices
        for(int i = 0; i < StripLen(stripverts); i++)
        {
            pstripindices[numstripindices++] = stripverts[i];
            vertcache.Add(1, stripverts[i]);
        }

        // free this guy and pop him off the list
        delete &stripverts;
        pstriplist->pop_front();
    }

    // add terminating zero
    pstripindices[numstripindices++] = 0;
    *ppstripindices = pstripindices;

    return numstripindices;
}

//=========================================================================
// Merge striplist into one big uberlist with (hopefully) optimal caching
//=========================================================================
int CStripper::CreateLongStrip(STRIPLIST *pstriplist, WORD **ppstripindices)
{
    // allow room for one strip length plus a possible 3 extra indices per
    // concatenated strip list plus the final 0
    int indexcount = ((int)pstriplist->size() * 3) + 2;

    // we're storing the strips in [size1 i1 i2 i3][size2 i4 i5 i6][0] format
	STRIPLIST::iterator istriplist;
    for( istriplist = pstriplist->begin(); istriplist != pstriplist->end(); ++istriplist)
    {
        indexcount += StripLen(**istriplist);
    }

    // alloc the space for all this stuff
    WORD *pstripindices = new WORD [indexcount];
    assert(pstripindices);

    CVertCache vertcache;
    int numstripindices = 0;

    // add first strip
    istriplist = pstriplist->begin();
    const STRIPVERTS &stripverts = **istriplist;

    // first strip should be cw
    assert(FIsStripCW(stripverts));

    for(int ivert = 0; ivert < StripLen(stripverts); ivert++)
    {
        pstripindices[numstripindices++] = stripverts[ivert];
        vertcache.Add(1, stripverts[ivert]);
    }

    // kill first dude
    delete &stripverts;
    pstriplist->erase(istriplist);

    // add all the others
    while(pstriplist->size())
    {
        istriplist = FindBestCachedStrip(pstriplist, vertcache);
        STRIPVERTS &stripverts = **istriplist;
        short lastvert = pstripindices[numstripindices - 1];
        short firstvert = stripverts[0];

        if(firstvert != lastvert)
        {
            // add degenerate from last strip
            pstripindices[numstripindices++] = lastvert;

            // add degenerate from our strip
            pstripindices[numstripindices++] = firstvert;
        }

        // if we're not orientated correctly, we need to add a degenerate
        if(FIsStripCW(stripverts) != !(numstripindices & 0x1))
        {
            // This shouldn't happen - we're currently trying very hard
            // to keep everything oriented correctly.
            assert(false);
            pstripindices[numstripindices++] = firstvert;
        }

        // add these verts
        for(int ivert = 0; ivert < StripLen(stripverts); ivert++)
        {
            pstripindices[numstripindices++] = stripverts[ivert];
            vertcache.Add(1, stripverts[ivert]);
        }

        // free these guys
        delete &stripverts;
        pstriplist->erase(istriplist);
    }

    *ppstripindices = pstripindices;
    return numstripindices;
}

//=========================================================================
// Build a (hopefully) optimal set of strips from a trilist
//=========================================================================
void CStripper::BuildStrips(STRIPLIST *pstriplist, int maxlen, bool flookahead)
{
    // temp indices storage
    const int ctmpverts = 1024;
    int pstripverts[ctmpverts + 1];
    int pstriptris[ctmpverts + 1];

    assert(maxlen <= ctmpverts);

    // clear all the used flags for the tris
    memset(m_pused, 0, sizeof(m_pused[0]) * m_numtris);

    bool fstartcw = true;
    for(;;)
    {
        int besttri = 0;
        int bestvert = 0;
        float bestratio = 2.0f;
        int bestneighborcount = INT_MAX;

		int tri;
        for( tri = 0; tri < m_numtris; tri++)
        {
            // if used the continue
            if(m_pused[tri])
                continue;

            // get the neighbor count
            int curneightborcount = GetNeighborCount(tri);
            assert(curneightborcount >= 0 && curneightborcount <= 3);

            // push all the singletons to the very end
            if(!curneightborcount)
                curneightborcount = 4;

            // if this guy has more neighbors than the current best - bail
            if(curneightborcount > bestneighborcount)
                continue;

            // try starting the strip with each of this tris verts
            for(int vert = 0; vert < 3; vert++)
            {
                int swaps;
                int len = CreateStrip(tri, vert, maxlen, &swaps, flookahead,
                    fstartcw, pstriptris, pstripverts);
                assert(len);

                float ratio = (len == 3) ? 1.0f : (float)swaps / len;

                // check if this ratio is better than what we've already got for
                // this neighborcount
                if((curneightborcount < bestneighborcount) ||
                    ((curneightborcount == bestneighborcount) && (ratio < bestratio)))
                {
                    bestneighborcount = curneightborcount;

                    besttri = tri;
                    bestvert = vert;
                    bestratio = ratio;
                }

            }
        }

        // no strips found?
        if(bestneighborcount == INT_MAX)
            break;

        // recreate this strip
        int swaps;
        int len = CreateStrip(besttri, bestvert, maxlen,
            &swaps, flookahead, fstartcw, pstriptris, pstripverts);
        assert(len);

        // mark the tris on the best strip as used
        for(tri = 0; tri < len; tri++)
            m_pused[pstriptris[tri]] = 1;

        // create a new STRIPVERTS and stuff in the indices
        STRIPVERTS *pstripvertices = new STRIPVERTS(len + 1);
        assert(pstripvertices);

        // store orientation in first entry
        for(tri = 0; tri < len; tri++)
            (*pstripvertices)[tri] = m_ptriangles[pstriptris[tri]][pstripverts[tri]];
        (*pstripvertices)[len] = fstartcw;

        // store the STRIPVERTS
        pstriplist->push_back(pstripvertices);

        // if strip was odd - swap orientation
        if((len & 0x1))
            fstartcw = !fstartcw;
    }

#ifdef _DEBUG
    // make sure all tris are used
    for(int t = 0; t < m_numtris; t++)
        assert(m_pused[t]);
#endif
}

//=========================================================================
// Guesstimate on the total index count for this list of strips
//=========================================================================
int EstimateStripCost(STRIPLIST *pstriplist)
{
    int count = 0;

    for(STRIPLIST::iterator istriplist = pstriplist->begin();
        istriplist != pstriplist->end();
        ++istriplist)
    {
        // add count of indices
        count += StripLen(**istriplist);
    }

    // assume 2 indices per strip to tack all these guys together
    return count + ((int)pstriplist->size() - 1) * 2;
}

//=========================================================================
// Initialize triangle information (edges, #neighbors, etc.)
//=========================================================================
void CStripper::InitTriangleInfo(int tri, int vert)
{
    WORD *ptriverts = &m_ptriangles[tri + 1][0];
    int vert1 = m_ptriangles[tri][(vert + 1) % 3];
    int vert2 = m_ptriangles[tri][vert];

    for(int itri = tri + 1; itri < m_numtris; itri++, ptriverts += 3)
    {
        if(m_pused[itri] != 0x7)
        {
            for(int ivert = 0; ivert < 3; ivert++)
            {
                if((ptriverts[ivert] == vert1) &&
                    (ptriverts[(ivert + 1) % 3] == vert2))
                {
                    // add the triangle info
                    m_ptriinfo[tri].neighbortri[vert] = itri;
                    m_ptriinfo[tri].neighboredge[vert] = ivert;
                    m_pused[tri] |= (1 << vert);

                    m_ptriinfo[itri].neighbortri[ivert] = tri;
                    m_ptriinfo[itri].neighboredge[ivert] = vert;
                    m_pused[itri] |= (1 << ivert);
                    return;
                }
            }
        }
    }
}

//=========================================================================
// CStripper ctor
//=========================================================================
CStripper::CStripper(int numtris, TRIANGLELIST ptriangles)
{
    // store trilist info
    m_numtris = numtris;
    m_ptriangles = ptriangles;

    m_pused = new int[numtris];
    assert(m_pused);
    m_ptriinfo = new TRIANGLEINFO[numtris];
    assert(m_ptriinfo);

    // init triinfo
	int itri;
    for( itri = 0; itri < numtris; itri++)
    {
        m_ptriinfo[itri].neighbortri[0] = -1;
        m_ptriinfo[itri].neighbortri[1] = -1;
        m_ptriinfo[itri].neighbortri[2] = -1;
    }

    // clear the used flag
    memset(m_pused, 0, sizeof(m_pused[0]) * m_numtris);

    // go through all the triangles and find edges, neighbor counts
    for(itri = 0; itri < numtris; itri++)
    {
        for(int ivert = 0; ivert < 3; ivert++)
        {
            if(!(m_pused[itri] & (1 << ivert)))
                InitTriangleInfo(itri, ivert);
        }
    }

    // clear the used flags from InitTriangleInfo
    memset(m_pused, 0, sizeof(m_pused[0]) * m_numtris);
}

//=========================================================================
// CStripper dtor
//=========================================================================
CStripper::~CStripper()
{
    // free stuff
    delete [] m_pused;
    m_pused = NULL;

    delete [] m_ptriinfo;
    m_ptriinfo = NULL;
}

//=========================================================================
// Add an index to the cache - returns true if it was added, false otherwise
//=========================================================================
bool CVertCache::Add(int strip, int vertindex)
{
    // find index in cache
    for(int iCache = 0; iCache < CACHE_SIZE; iCache++)
    {
        if(vertindex == m_rgCache[iCache])
        {
            // if it's in the cache and it's from a different strip
            // change the strip to the new one and count the cache hit
            if(strip != m_rgCacheStrip[iCache])
            {
                m_cachehits++;
                m_rgCacheStrip[iCache] = strip;
                return true;
            }

            // we added this item to the cache earlier - carry on
            return false;
        }
    }

    // not in cache, add vert and strip
    m_rgCache[m_iCachePtr] = vertindex;
    m_rgCacheStrip[m_iCachePtr] = strip;
    m_iCachePtr = (m_iCachePtr + 1) % CACHE_SIZE;
    return true;
}

#ifdef _DEBUG
//=========================================================================
// Turn on c runtime leak checking, etc.
//=========================================================================
void EnableLeakChecking()
{
    int flCrtDbgFlags = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);

    flCrtDbgFlags &=
        ~(_CRTDBG_LEAK_CHECK_DF |
        _CRTDBG_CHECK_ALWAYS_DF |
        _CRTDBG_DELAY_FREE_MEM_DF);

    // always check for memory leaks
    flCrtDbgFlags |= _CRTDBG_LEAK_CHECK_DF;

    // others you may / may not want to set
    flCrtDbgFlags |= _CRTDBG_CHECK_ALWAYS_DF;
    flCrtDbgFlags |= _CRTDBG_DELAY_FREE_MEM_DF;

    _CrtSetDbgFlag(flCrtDbgFlags);

    // all types of reports go via OutputDebugString
    _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG);
    _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
    _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG);

    // big errors and asserts get their own assert window
    _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_WNDW);
    _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_WNDW);

    // _CrtSetBreakAlloc(0);
}
#endif

//=========================================================================
// Main Stripify routine
//=========================================================================
int Stripify(int numtris, WORD *ptriangles, int *pnumindices, WORD **ppstripindices)
{
    if(!numtris || !ptriangles)
        return 0;

#ifdef _DEBUG
//    EnableLeakChecking();
#endif

    CStripper stripper(numtris, (TRIANGLELIST)ptriangles);

    // map of various args to try stripifying mesh with
    struct ARGMAP
    {
        int     maxlen;         // maximum length of strips
        bool    flookahead;     // use sgi greedy lookahead (or not)
    } rgargmap[] =
    {
        { 1024,  true  },
        { 1024,  false },
    };
    static const int cargmaps = sizeof(rgargmap) / sizeof(rgargmap[0]);
    STRIPLIST   striplistbest;
    int         bestlistcost = 0;

    for(int imap = 0; imap < cargmaps; imap++)
    {
        STRIPLIST striplist;

        // build the strip with the various args
        stripper.BuildStrips(&striplist, rgargmap[imap].maxlen,
            rgargmap[imap].flookahead);

        // guesstimate the list cost and store it if it's good
        int listcost = EstimateStripCost(&striplist);
        if(!bestlistcost || (listcost < bestlistcost))
        {
            // free the old best list
            FreeStripListVerts(&striplistbest);

            // store the new best list
            striplistbest = striplist;
            bestlistcost = listcost;
            assert(bestlistcost > 0);
        }
        else
        {
            FreeStripListVerts(&striplist);
        }
    }

#ifdef NEVER
    // Return the strips in [size1 i1 i2 i3][size2 i4 i5 i6]...[0] format
    // Very useful for debugging...
    return stripper.CreateManyStrips(&striplistbest, ppstripindices);
#endif // NEVER

    // return one big long strip
    int numindices = stripper.CreateLongStrip(&striplistbest, ppstripindices);

    if(pnumindices)
        *pnumindices = numindices;
    return numindices;
}

//=========================================================================
// Class used to vertices for locality of access.
//=========================================================================
struct SortEntry
{
public:
    int iFirstUsed;
    int iOrigIndex;

    bool operator<(const SortEntry& rhs)
    {
        return iFirstUsed < rhs.iFirstUsed;
    }
};

//=========================================================================
// Reorder the vertices
//=========================================================================
void ComputeVertexPermutation(int numstripindices, WORD* pstripindices,
                              int* pnumverts, WORD** ppvertexpermutation)
{
    // Sort verts to maximize locality.
    SortEntry* pSortTable = new SortEntry[*pnumverts];

    // Fill in original index.
	int i;
    for( i = 0; i < *pnumverts; i++)
    {
        pSortTable[i].iOrigIndex = i;
        pSortTable[i].iFirstUsed = -1;
    }

    // Fill in first used flag.
    for(i = 0; i < numstripindices; i++)
    {
        int index = pstripindices[i];

        if(pSortTable[index].iFirstUsed == -1)
            pSortTable[index].iFirstUsed = i;
    }

    // Sort the table.
    sort(pSortTable, pSortTable + *pnumverts);

    // Copy re-mapped to orignal vertex permutaion into output array.
    *ppvertexpermutation = new WORD[*pnumverts];

    for(i = 0; i < *pnumverts; i++)
    {
        (*ppvertexpermutation)[i] = pSortTable[i].iOrigIndex;
    }

    // Build original to re-mapped permutation.
    WORD* pInversePermutation = new WORD[numstripindices];

    for(i = 0; i < *pnumverts; i++)
    {
        pInversePermutation[pSortTable[i].iOrigIndex] = i;
    }

    // We need to remap indices as well.
    for(i = 0; i < numstripindices; i++)
    {
        pstripindices[i] = pInversePermutation[pstripindices[i]];
    }

    delete[] pSortTable;
    delete[] pInversePermutation;
}