aboutsummaryrefslogtreecommitdiff
path: root/mp/src/utils/common/mstristrip.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /mp/src/utils/common/mstristrip.cpp
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/utils/common/mstristrip.cpp')
-rw-r--r--mp/src/utils/common/mstristrip.cpp1860
1 files changed, 930 insertions, 930 deletions
diff --git a/mp/src/utils/common/mstristrip.cpp b/mp/src/utils/common/mstristrip.cpp
index 9e611f94..ad6ebdc8 100644
--- a/mp/src/utils/common/mstristrip.cpp
+++ b/mp/src/utils/common/mstristrip.cpp
@@ -1,930 +1,930 @@
-//========= 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) const
- {
- 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;
-}
-
+//========= 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) const
+ {
+ 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;
+}
+