123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783 |
- /*
- NvMeshIslandGeneration.cpp : This code snippet walks the toplogy of a triangle mesh and detects the set of unique connected 'mesh islands'
- */
- /*!
- **
- ** Copyright (c) 2009 by John W. Ratcliff mailto:[email protected]
- **
- ** Portions of this source has been released with the PhysXViewer application, as well as
- ** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
- **
- ** If you find this code useful or you are feeling particularily generous I would
- ** ask that you please go to http://www.amillionpixels.us and make a donation
- ** to Troy DeMolay.
- **
- ** DeMolay is a youth group for young men between the ages of 12 and 21.
- ** It teaches strong moral principles, as well as leadership skills and
- ** public speaking. The donations page uses the 'pay for pixels' paradigm
- ** where, in this case, a pixel is only a single penny. Donations can be
- ** made for as small as $4 or as high as a $100 block. Each person who donates
- ** will get a link to their own site as well as acknowledgement on the
- ** donations blog located here http://www.amillionpixels.blogspot.com/
- **
- ** If you wish to contact me you can use the following methods:
- **
- ** Skype ID: jratcliff63367
- ** Yahoo: jratcliff63367
- ** AOL: jratcliff1961
- ** email: [email protected]
- **
- **
- ** The MIT license:
- **
- ** Permission is hereby granted, free of charge, to any person obtaining a copy
- ** of this software and associated documentation files (the "Software"), to deal
- ** in the Software without restriction, including without limitation the rights
- ** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- ** copies of the Software, and to permit persons to whom the Software is furnished
- ** to do so, subject to the following conditions:
- **
- ** The above copyright notice and this permission notice shall be included in all
- ** copies or substantial portions of the Software.
- ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- ** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- ** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <assert.h>
- #pragma warning(disable:4100 4288)
- #include "NvMeshIslandGeneration.h"
- #include "NvFloatMath.h"
- #include "NvHashMap.h"
- namespace CONVEX_DECOMPOSITION
- {
- typedef CONVEX_DECOMPOSITION::Array< NxU32 > NxU32Vector;
- class Edge;
- class Island;
- class AABB
- {
- public:
- NxF32 mMin[3];
- NxF32 mMax[3];
- };
- class Triangle
- {
- public:
- Triangle(void)
- {
- mConsumed = false;
- mIsland = 0;
- mHandle = 0;
- mId = 0;
- }
- void minmax(const NxF32 *p,AABB &box)
- {
- if ( p[0] < box.mMin[0] ) box.mMin[0] = p[0];
- if ( p[1] < box.mMin[1] ) box.mMin[1] = p[1];
- if ( p[2] < box.mMin[2] ) box.mMin[2] = p[2];
- if ( p[0] > box.mMax[0] ) box.mMax[0] = p[0];
- if ( p[1] > box.mMax[1] ) box.mMax[1] = p[1];
- if ( p[2] > box.mMax[2] ) box.mMax[2] = p[2];
- }
- void minmax(const NxF64 *p,AABB &box)
- {
- if ( (NxF32)p[0] < box.mMin[0] ) box.mMin[0] = (NxF32)p[0];
- if ( (NxF32)p[1] < box.mMin[1] ) box.mMin[1] = (NxF32)p[1];
- if ( (NxF32)p[2] < box.mMin[2] ) box.mMin[2] = (NxF32)p[2];
- if ( (NxF32)p[0] > box.mMax[0] ) box.mMax[0] = (NxF32)p[0];
- if ( (NxF32)p[1] > box.mMax[1] ) box.mMax[1] = (NxF32)p[1];
- if ( (NxF32)p[2] > box.mMax[2] ) box.mMax[2] = (NxF32)p[2];
- }
- void buildBox(const NxF32 *vertices_f,const NxF64 *vertices_d,NxU32 id);
- void render(NxU32 color)
- {
- // gRenderDebug->DebugBound(&mBox.mMin[0],&mBox.mMax[0],color,60.0f);
- }
- void getTriangle(NxF32 *tri,const NxF32 *vertices_f,const NxF64 *vertices_d);
- NxU32 mHandle;
- bool mConsumed;
- Edge *mEdges[3];
- Island *mIsland; // identifies which island it is a member of
- unsigned short mId;
- AABB mBox;
- };
- class Edge
- {
- public:
- Edge(void)
- {
- mI1 = 0;
- mI2 = 0;
- mHash = 0;
- mNext = 0;
- mPrevious = 0;
- mParent = 0;
- mNextTriangleEdge = 0;
- }
- void init(NxU32 i1,NxU32 i2,Triangle *parent)
- {
- assert( i1 < 65536 );
- assert( i2 < 65536 );
- mI1 = i1;
- mI2 = i2;
- mHash = (i2<<16)|i1;
- mReverseHash = (i1<<16)|i2;
- mNext = 0;
- mPrevious = 0;
- mParent = parent;
- }
- NxU32 mI1;
- NxU32 mI2;
- NxU32 mHash;
- NxU32 mReverseHash;
- Edge *mNext;
- Edge *mPrevious;
- Edge *mNextTriangleEdge;
- Triangle *mParent;
- };
- typedef CONVEX_DECOMPOSITION::HashMap< NxU32, Edge * > EdgeHashMap;
- typedef CONVEX_DECOMPOSITION::Array< Triangle * > TriangleVector;
- class EdgeCheck
- {
- public:
- EdgeCheck(Triangle *t,Edge *e)
- {
- mTriangle = t;
- mEdge = e;
- }
- Triangle *mTriangle;
- Edge *mEdge;
- };
- typedef CONVEX_DECOMPOSITION::Array< EdgeCheck > EdgeCheckQueue;
- class Island
- {
- public:
- Island(Triangle *t,Triangle *root)
- {
- mVerticesFloat = 0;
- mVerticesDouble = 0;
- t->mIsland = this;
- mTriangles.pushBack(t);
- mCoplanar = false;
- fm_initMinMax(mMin,mMax);
- }
- void add(Triangle *t,Triangle *root)
- {
- t->mIsland = this;
- mTriangles.pushBack(t);
- }
- void merge(Island &isl)
- {
- TriangleVector::Iterator i;
- for (i=isl.mTriangles.begin(); i!=isl.mTriangles.end(); ++i)
- {
- Triangle *t = (*i);
- mTriangles.pushBack(t);
- }
- isl.mTriangles.clear();
- }
- bool isTouching(Island *isl,const NxF32 *vertices_f,const NxF64 *vertices_d)
- {
- bool ret = false;
- mVerticesFloat = vertices_f;
- mVerticesDouble = vertices_d;
- if ( fm_intersectAABB(mMin,mMax,isl->mMin,isl->mMax) ) // if the two islands has an intersecting AABB
- {
- // todo..
- }
- return ret;
- }
- void SAP_DeletePair(const void* object0, const void* object1, void* user_data, void* pair_user_data)
- {
- }
- void render(NxU32 color)
- {
- // gRenderDebug->DebugBound(mMin,mMax,color,60.0f);
- TriangleVector::Iterator i;
- for (i=mTriangles.begin(); i!=mTriangles.end(); ++i)
- {
- Triangle *t = (*i);
- t->render(color);
- }
- }
- const NxF64 *mVerticesDouble;
- const NxF32 *mVerticesFloat;
- NxF32 mMin[3];
- NxF32 mMax[3];
- bool mCoplanar; // marked as co-planar..
- TriangleVector mTriangles;
- };
- void Triangle::getTriangle(NxF32 *tri,const NxF32 *vertices_f,const NxF64 *vertices_d)
- {
- NxU32 i1 = mEdges[0]->mI1;
- NxU32 i2 = mEdges[1]->mI1;
- NxU32 i3 = mEdges[2]->mI1;
- if ( vertices_f )
- {
- const NxF32 *p1 = &vertices_f[i1*3];
- const NxF32 *p2 = &vertices_f[i2*3];
- const NxF32 *p3 = &vertices_f[i3*3];
- fm_copy3(p1,tri);
- fm_copy3(p2,tri+3);
- fm_copy3(p3,tri+6);
- }
- else
- {
- const NxF64 *p1 = &vertices_d[i1*3];
- const NxF64 *p2 = &vertices_d[i2*3];
- const NxF64 *p3 = &vertices_d[i3*3];
- fm_doubleToFloat3(p1,tri);
- fm_doubleToFloat3(p2,tri+3);
- fm_doubleToFloat3(p3,tri+6);
- }
- }
- void Triangle::buildBox(const NxF32 *vertices_f,const NxF64 *vertices_d,NxU32 id)
- {
- mId = (unsigned short)id;
- NxU32 i1 = mEdges[0]->mI1;
- NxU32 i2 = mEdges[1]->mI1;
- NxU32 i3 = mEdges[2]->mI1;
- if ( vertices_f )
- {
- const NxF32 *p1 = &vertices_f[i1*3];
- const NxF32 *p2 = &vertices_f[i2*3];
- const NxF32 *p3 = &vertices_f[i3*3];
- mBox.mMin[0] = p1[0];
- mBox.mMin[1] = p1[1];
- mBox.mMin[2] = p1[2];
- mBox.mMax[0] = p1[0];
- mBox.mMax[1] = p1[1];
- mBox.mMax[2] = p1[2];
- minmax(p2,mBox);
- minmax(p3,mBox);
- }
- else
- {
- const NxF64 *p1 = &vertices_d[i1*3];
- const NxF64 *p2 = &vertices_d[i2*3];
- const NxF64 *p3 = &vertices_d[i3*3];
- mBox.mMin[0] = (NxF32)p1[0];
- mBox.mMin[1] = (NxF32)p1[1];
- mBox.mMin[2] = (NxF32)p1[2];
- mBox.mMax[0] = (NxF32)p1[0];
- mBox.mMax[1] = (NxF32)p1[1];
- mBox.mMax[2] = (NxF32)p1[2];
- minmax(p2,mBox);
- minmax(p3,mBox);
- }
- assert(mIsland);
- if ( mIsland )
- {
- if ( mBox.mMin[0] < mIsland->mMin[0] ) mIsland->mMin[0] = mBox.mMin[0];
- if ( mBox.mMin[1] < mIsland->mMin[1] ) mIsland->mMin[1] = mBox.mMin[1];
- if ( mBox.mMin[2] < mIsland->mMin[2] ) mIsland->mMin[2] = mBox.mMin[2];
- if ( mBox.mMax[0] > mIsland->mMax[0] ) mIsland->mMax[0] = mBox.mMax[0];
- if ( mBox.mMax[1] > mIsland->mMax[1] ) mIsland->mMax[1] = mBox.mMax[1];
- if ( mBox.mMax[2] > mIsland->mMax[2] ) mIsland->mMax[2] = mBox.mMax[2];
- }
- }
- typedef CONVEX_DECOMPOSITION::Array< Island * > IslandVector;
- class MyMeshIslandGeneration : public MeshIslandGeneration
- {
- public:
- MyMeshIslandGeneration(void)
- {
- mTriangles = 0;
- mEdges = 0;
- mVerticesDouble = 0;
- mVerticesFloat = 0;
- }
- ~MyMeshIslandGeneration(void)
- {
- reset();
- }
- void reset(void)
- {
- delete []mTriangles;
- delete []mEdges;
- mTriangles = 0;
- mEdges = 0;
- mTriangleEdges.clear();
- IslandVector::Iterator i;
- for (i=mIslands.begin(); i!=mIslands.end(); ++i)
- {
- Island *_i = (*i);
- delete _i;
- }
- mIslands.clear();
- }
- NxU32 islandGenerate(NxU32 tcount,const NxU32 *indices,const NxF64 *vertices)
- {
- mVerticesDouble = vertices;
- mVerticesFloat = 0;
- return islandGenerate(tcount,indices);
- }
- NxU32 islandGenerate(NxU32 tcount,const NxU32 *indices,const NxF32 *vertices)
- {
- mVerticesDouble = 0;
- mVerticesFloat = vertices;
- return islandGenerate(tcount,indices);
- }
- NxU32 islandGenerate(NxU32 tcount,const NxU32 *indices)
- {
- NxU32 ret = 0;
- reset();
- mTcount = tcount;
- mTriangles = new Triangle[tcount];
- mEdges = new Edge[tcount*3];
- Edge *e = mEdges;
- for (NxU32 i=0; i<tcount; i++)
- {
- Triangle &t = mTriangles[i];
- NxU32 i1 = *indices++;
- NxU32 i2 = *indices++;
- NxU32 i3 = *indices++;
- t.mEdges[0] = e;
- t.mEdges[1] = e+1;
- t.mEdges[2] = e+2;
- e = addEdge(e,&t,i1,i2);
- e = addEdge(e,&t,i2,i3);
- e = addEdge(e,&t,i3,i1);
- }
- // while there are still edges to process...
- while ( mTriangleEdges.size() != 0 )
- {
- EdgeHashMap::Iterator iter = mTriangleEdges.getIterator();
- Triangle *t = iter->second->mParent;
- Island *i = new Island(t,mTriangles); // the initial triangle...
- removeTriangle(t); // remove this triangle from the triangle-edges hashmap
- mIslands.pushBack(i);
- // now keep adding to this island until we can no longer walk any shared edges..
- addEdgeCheck(t,t->mEdges[0]);
- addEdgeCheck(t,t->mEdges[1]);
- addEdgeCheck(t,t->mEdges[2]);
- while ( !mEdgeCheckQueue.empty() )
- {
- EdgeCheck e = mEdgeCheckQueue.popBack();
- // Process all triangles which share this edge
- Edge *edge = locateSharedEdge(e.mEdge);
- while ( edge )
- {
- Triangle *t = edge->mParent;
- assert(!t->mConsumed);
- i->add(t,mTriangles);
- removeTriangle(t); // remove this triangle from the triangle-edges hashmap
- // now keep adding to this island until we can no longer walk any shared edges..
- if ( edge != t->mEdges[0] )
- {
- addEdgeCheck(t,t->mEdges[0]);
- }
- if ( edge != t->mEdges[1] )
- {
- addEdgeCheck(t,t->mEdges[1]);
- }
- if ( edge != t->mEdges[2] )
- {
- addEdgeCheck(t,t->mEdges[2]);
- }
- edge = locateSharedEdge(e.mEdge); // keep going until all shared edges have been processed!
- }
- }
- }
- ret = (NxU32)mIslands.size();
- return ret;
- }
- NxU32 * getIsland(NxU32 index,NxU32 &otcount)
- {
- NxU32 *ret = 0;
- mIndices.clear();
- if ( index < mIslands.size() )
- {
- Island *i = mIslands[index];
- otcount = (NxU32)i->mTriangles.size();
- TriangleVector::Iterator j;
- for (j=i->mTriangles.begin(); j!=i->mTriangles.end(); ++j)
- {
- Triangle *t = (*j);
- mIndices.pushBack(t->mEdges[0]->mI1);
- mIndices.pushBack(t->mEdges[1]->mI1);
- mIndices.pushBack(t->mEdges[2]->mI1);
- }
- ret = &mIndices[0];
- }
- return ret;
- }
- private:
- void removeTriangle(Triangle *t)
- {
- t->mConsumed = true;
- removeEdge(t->mEdges[0]);
- removeEdge(t->mEdges[1]);
- removeEdge(t->mEdges[2]);
- }
- Edge * locateSharedEdge(Edge *e)
- {
- Edge *ret = 0;
- const EdgeHashMap::Entry *found = mTriangleEdges.find( e->mReverseHash );
- if ( found != NULL )
- {
- ret = (*found).second;
- assert( ret->mHash == e->mReverseHash );
- }
- return ret;
- }
- void removeEdge(Edge *e)
- {
- const EdgeHashMap::Entry *found = mTriangleEdges.find( e->mHash );
- if ( found != NULL )
- {
- Edge *prev = 0;
- Edge *scan = (*found).second;
- while ( scan && scan != e )
- {
- prev = scan;
- scan = scan->mNextTriangleEdge;
- }
- if ( scan )
- {
- if ( prev == 0 )
- {
- if ( scan->mNextTriangleEdge )
- {
- mTriangleEdges.erase(e->mHash);
- mTriangleEdges[e->mHash] = scan->mNextTriangleEdge;
- }
- else
- {
- mTriangleEdges.erase(e->mHash); // no more polygons have an edge here
- }
- }
- else
- {
- prev->mNextTriangleEdge = scan->mNextTriangleEdge;
- }
- }
- else
- {
- assert(0);
- }
- }
- else
- {
- assert(0); // impossible!
- }
- }
- Edge * addEdge(Edge *e,Triangle *t,NxU32 i1,NxU32 i2)
- {
- e->init(i1,i2,t);
- const EdgeHashMap::Entry *found = mTriangleEdges.find(e->mHash);
- if ( found == NULL )
- {
- mTriangleEdges[ e->mHash ] = e;
- }
- else
- {
- Edge *pn = (*found).second;
- e->mNextTriangleEdge = pn;
- mTriangleEdges.erase(e->mHash);
- mTriangleEdges[e->mHash] = e;
- }
- e++;
- return e;
- }
- void addEdgeCheck(Triangle *t,Edge *e)
- {
- EdgeCheck ec(t,e);
- mEdgeCheckQueue.pushBack(ec);
- }
- NxU32 mergeCoplanarIslands(const NxF32 *vertices)
- {
- mVerticesFloat = vertices;
- mVerticesDouble = 0;
- return mergeCoplanarIslands();
- }
- NxU32 mergeCoplanarIslands(const NxF64 *vertices)
- {
- mVerticesDouble = vertices;
- mVerticesFloat = 0;
- return mergeCoplanarIslands();
- }
- // this island needs to be merged
- void mergeTouching(Island *isl)
- {
- Island *touching = 0;
- IslandVector::Iterator i;
- for (i=mIslands.begin(); i!=mIslands.end(); ++i)
- {
- Island *_i = (*i);
- if ( !_i->mCoplanar ) // can't merge with coplanar islands!
- {
- if ( _i->isTouching(isl,mVerticesFloat,mVerticesDouble) )
- {
- touching = _i;
- }
- }
- }
- }
- NxU32 mergeCoplanarIslands(void)
- {
- NxU32 ret = 0;
- if ( !mIslands.empty() )
- {
- NxU32 cp_count = 0;
- NxU32 npc_count = 0;
- NxU32 count = (NxU32)mIslands.size();
- for (NxU32 i=0; i<count; i++)
- {
- NxU32 otcount;
- const NxU32 *oindices = getIsland(i,otcount);
- if ( otcount )
- {
- bool isCoplanar;
- if ( mVerticesFloat )
- isCoplanar = fm_isMeshCoplanar(otcount, oindices, mVerticesFloat, true);
- else
- isCoplanar = fm_isMeshCoplanar(otcount, oindices, mVerticesDouble, true);
- if ( isCoplanar )
- {
- Island *isl = mIslands[i];
- isl->mCoplanar = true;
- cp_count++;
- }
- else
- {
- npc_count++;
- }
- }
- else
- {
- assert(0);
- }
- }
- if ( cp_count )
- {
- if ( npc_count == 0 ) // all islands are co-planar!
- {
- IslandVector temp = mIslands;
- mIslands.clear();
- Island *isl = mIslands[0];
- mIslands.pushBack(isl);
- for (NxU32 i=1; i<cp_count; i++)
- {
- Island *_i = mIslands[i];
- isl->merge(*_i);
- delete _i;
- }
- }
- else
- {
- Triangle *t = mTriangles;
- for (NxU32 i=0; i<mTcount; i++)
- {
- t->buildBox(mVerticesFloat,mVerticesDouble,i);
- t++;
- }
- IslandVector::Iterator i;
- for (i=mIslands.begin(); i!=mIslands.end(); ++i)
- {
- Island *isl = (*i);
- NxU32 color = 0x00FF00;
- if ( isl->mCoplanar )
- {
- color = 0xFFFF00;
- }
- mergeTouching(isl);
- }
- IslandVector temp = mIslands;
- mIslands.clear();
- for (i=temp.begin(); i!=temp.end(); i++)
- {
- Island *isl = (*i);
- if ( isl->mCoplanar )
- {
- delete isl; // kill it
- }
- else
- {
- mIslands.pushBack(isl);
- }
- }
- ret = (NxU32)mIslands.size();
- }
- }
- else
- {
- ret = npc_count;
- }
- }
- return ret;
- }
- NxU32 mergeTouchingIslands(const NxF32 *vertices)
- {
- NxU32 ret = 0;
- return ret;
- }
- NxU32 mergeTouchingIslands(const NxF64 *vertices)
- {
- NxU32 ret = 0;
- return ret;
- }
- NxU32 mTcount;
- Triangle *mTriangles;
- Edge *mEdges;
- EdgeHashMap mTriangleEdges;
- IslandVector mIslands;
- EdgeCheckQueue mEdgeCheckQueue;
- const NxF64 *mVerticesDouble;
- const NxF32 *mVerticesFloat;
- NxU32Vector mIndices;
- };
- MeshIslandGeneration * createMeshIslandGeneration(void)
- {
- MyMeshIslandGeneration *mig = new MyMeshIslandGeneration;
- return static_cast< MeshIslandGeneration *>(mig);
- }
- void releaseMeshIslandGeneration(MeshIslandGeneration *cm)
- {
- MyMeshIslandGeneration *mig = static_cast< MyMeshIslandGeneration *>(cm);
- delete mig;
- }
- }; // end of namespace
|