diff --git a/DSMeshConvert/DSMeshConvert.vcxproj b/DSMeshConvert/DSMeshConvert.vcxproj index d8993b8..5c52abd 100644 --- a/DSMeshConvert/DSMeshConvert.vcxproj +++ b/DSMeshConvert/DSMeshConvert.vcxproj @@ -59,7 +59,7 @@ EditAndContinue - assimp.lib;%(AdditionalDependencies) + assimp.lib;%(AdditionalDependencies);OpenGL32.lib;glu32.lib assimp--1.1.700-sdk\lib\assimp_debug-dll_win32;%(AdditionalLibraryDirectories) true MachineX86 @@ -86,6 +86,7 @@ + @@ -95,9 +96,11 @@ + + @@ -106,6 +109,7 @@ + diff --git a/DSMeshConvert/DSMeshConvert.vcxproj.filters b/DSMeshConvert/DSMeshConvert.vcxproj.filters index 003e1ea..a001a47 100644 --- a/DSMeshConvert/DSMeshConvert.vcxproj.filters +++ b/DSMeshConvert/DSMeshConvert.vcxproj.filters @@ -38,6 +38,8 @@ + + @@ -66,5 +68,7 @@ + + \ No newline at end of file diff --git a/DSMeshConvert/List.h b/DSMeshConvert/List.h new file mode 100644 index 0000000..1f8e0c3 --- /dev/null +++ b/DSMeshConvert/List.h @@ -0,0 +1,491 @@ +/****************************************************************************** +* +* List.h +* +* +* By Patrick Wyatt - 5/16/2010 +* +***/ + + +/****************************************************************************** +* +* WHAT IT IS +* +* This module defines a linked-list implementation that uses "embedded" +* links rather than separately allocated link-nodes as does STL and +* more or less all other linked-list implementations. +* +* Why is this cool: +* 1. No additional memory allocations (malloc) required to link +* an object into a linked list. +* 2. Not necessary to traverse an additional pointer references +* to get to the object being dereferenced. +* 3. Probably most importantly, when objects get deleted, they +* automatically unlink themselves from the lists they're +* linked to, eliminating many common types of bugs. +* +* HOW TO USE IT +* +* Declare a structure that will be contained in one or more linked lists: +* class CFoo { +* LIST_LINK(CFoo) m_linkByData; +* LIST_LINK(CFoo) m_linkByType; +* int m_data; +* int m_type; +* ... +* }; +* +* Declare list variables: +* LIST_DECLARE(CFoo, m_linkByData) listByData; +* LIST_DECLARE(CFoo, m_linkByType) listByType; +* LIST_PTR(CFoo) m_listPtr = foo ? &listByData : &listByType; +* +* Operations on links: +* T * Prev (); +* T * Next (); +* void Unlink (); +* bool IsLinked () const; +* +* Operations on lists: +* bool Empty () const; +* void UnlinkAll (); +* void DeleteAll (); +* +* T * Head (); +* T * Tail (); +* T * Prev (T * node); +* T * Next (T * node); +* +* void InsertHead (T * node); +* void InsertTail (T * node); +* void InsertBefore (T * node, T * before); +* void InsertAfter (T * node, T * after); +* +* NOTES +* +* Limitations: +* All nodes must be allocated on (at least) two-byte boundaries +* because the low bit is used as a sentinel to signal end-of-list. +* +* Thanks to: +* Something like this code was originally implemented by Mike +* O'Brien in storm.dll for Diablo in 1995, and again at ArenaNet +* for Guild Wars. +* +***/ + + +#ifdef LIST_H +#error "Header included more than once" +#endif +#define LIST_H + + +/****************************************************************************** +* +* List definition macros +* +***/ + +// Define a linked list: +// T = type of object being linked +// link = member within object which is the link field +#define LIST_DECLARE(T, link) TListDeclare + +// Define a field within a structure that will be used to link it into a list +#define LIST_LINK(T) TLink + +// Define a pointer to a list +#define LIST_PTR(T) TList * + + +/****************************************************************************** +* +* TLink +* +***/ + +//============================================================================= +template +class TLink { +public: + ~TLink (); + TLink (); + + bool IsLinked () const; + void Unlink (); + + T * Prev (); + T * Next (); + const T * Prev () const; + const T * Next () const; + + // For use by list-type classes, not user code; + // the alternative is to friend TList, THash, + // and (eventually) many other structures. + TLink (size_t offset); + void SetOffset (size_t offset); + TLink * NextLink (); + TLink * PrevLink (); + void InsertBefore (T * node, TLink * nextLink); + void InsertAfter (T * node, TLink * prevLink); + +private: + T * m_nextNode; // pointer to the next >object< + TLink * m_prevLink; // pointer to the previous >link field< + void RemoveFromList (); + + // Hide copy-constructor and assignment operator + TLink (const TLink &); + TLink & operator= (const TLink &); +}; + +//============================================================================= +template +TLink::~TLink () { + RemoveFromList(); +} + +//============================================================================= +template +TLink::TLink () { + // Mark this node as the end of the list, with no link offset + m_nextNode = (T *) ((size_t) this + 1 - 0); + m_prevLink = this; +} + +//============================================================================= +template +TLink::TLink (size_t offset) { + // Mark this node as the end of the list, with the link offset set + m_nextNode = (T *) ((size_t) this + 1 - offset); + m_prevLink = this; +} + +//============================================================================= +template +void TLink::SetOffset (size_t offset) { + // Mark this node as the end of the list, with the link offset set + m_nextNode = (T *) ((size_t) this + 1 - offset); + m_prevLink = this; +} + +//============================================================================= +template +TLink * TLink::NextLink () { + // Calculate the offset from a node pointer to a link structure + size_t offset = (size_t) this - ((size_t) m_prevLink->m_nextNode & ~1); + + // Get the link field for the next node + return (TLink *) (((size_t) m_nextNode & ~1) + offset); +} + +//============================================================================= +template +void TLink::RemoveFromList () { + NextLink()->m_prevLink = m_prevLink; + m_prevLink->m_nextNode = m_nextNode; +} + +//============================================================================= +template +void TLink::InsertBefore (T * node, TLink * nextLink) { + RemoveFromList(); + + m_prevLink = nextLink->m_prevLink; + m_nextNode = m_prevLink->m_nextNode; + + nextLink->m_prevLink->m_nextNode = node; + nextLink->m_prevLink = this; +} + +//============================================================================= +template +void TLink::InsertAfter (T * node, TLink * prevLink) { + RemoveFromList(); + + m_prevLink = prevLink; + m_nextNode = prevLink->m_nextNode; + + prevLink->NextLink()->m_prevLink = this; + prevLink->m_nextNode = node; +} + +//============================================================================= +template +bool TLink::IsLinked () const { + return m_prevLink != this; +} + +//============================================================================= +template +void TLink::Unlink () { + RemoveFromList(); + + // Mark this node as the end of the list with no link offset + m_nextNode = (T *) ((size_t) this + 1); + m_prevLink = this; +} + +//============================================================================= +template +TLink * TLink::PrevLink () { + return m_prevLink; +} + +//============================================================================= +template +T * TLink::Prev () { + T * prevNode = m_prevLink->m_prevLink->m_nextNode; + if ((size_t) prevNode & 1) + return NULL; + return prevNode; +} + +//============================================================================= +template +const T * TLink::Prev () const { + const T * prevNode = m_prevLink->m_prevLink->m_nextNode; + if ((size_t) prevNode & 1) + return NULL; + return prevNode; +} + +//============================================================================= +template +T * TLink::Next () { + if ((size_t) m_nextNode & 1) + return NULL; + return m_nextNode; +} + +//============================================================================= +template +const T * TLink::Next () const { + if ((size_t) m_nextNode & 1) + return NULL; + return m_nextNode; +} + + +/****************************************************************************** +* +* TList +* +***/ + +//============================================================================= +template +class TList { +public: + ~TList (); + TList (); + + bool Empty () const; + void UnlinkAll (); + void DeleteAll (); + + T * Head (); + T * Tail (); + const T * Head () const; + const T * Tail () const; + + T * Prev (T * node); + T * Next (T * node); + const T * Prev (const T * node) const; + const T * Next (const T * node) const; + + void InsertHead (T * node); + void InsertTail (T * node); + void InsertBefore (T * node, T * before); + void InsertAfter (T * node, T * after); + +private: + TLink m_link; + size_t m_offset; + + TList (size_t offset); + TLink * GetLinkFromNode (const T * node) const; + template friend class TListDeclare; + + // Hide copy-constructor and assignment operator + TList (const TList &); + TList & operator= (const TList &); +}; + +//============================================================================= +template +TList::~TList () { + UnlinkAll(); +} + +//============================================================================= +template +TList::TList () : + m_link(), + m_offset((size_t) -1) +{} + +//============================================================================= +template +TList::TList (size_t offset) : + m_link(offset), + m_offset(offset) +{} + +//============================================================================= +template +bool TList::Empty () const { + return m_link.Next() == NULL; +} + +//============================================================================= +template +void TList::UnlinkAll () { + for (;;) { + TLink * link = m_link.PrevLink(); + if (link == &m_link) + break; + link->Unlink(); + } +} + +//============================================================================= +template +void TList::DeleteAll () { + while (T * node = m_link.Next()) + delete node; +} + +//============================================================================= +template +T * TList::Head () { + return m_link.Next(); +} + +//============================================================================= +template +T * TList::Tail () { + return m_link.Prev(); +} + +//============================================================================= +template +const T * TList::Head () const { + return m_link.Next(); +} + +//============================================================================= +template +const T * TList::Tail () const { + return m_link.Prev(); +} + +//============================================================================= +template +T * TList::Prev (T * node) { + return GetLinkFromNode(node)->Prev(); +} + +//============================================================================= +template +const T * TList::Prev (const T * node) const { + return GetLinkFromNode(node)->Prev(); +} + +//============================================================================= +template +T * TList::Next (T * node) { + return GetLinkFromNode(node)->Next(); +} + +//============================================================================= +template +const T * TList::Next (const T * node) const { + return GetLinkFromNode(node)->Next(); +} + +//============================================================================= +template +void TList::InsertHead (T * node) { + InsertAfter(node, NULL); +} + +//============================================================================= +template +void TList::InsertTail (T * node) { + InsertBefore(node, NULL); +} + +//============================================================================= +template +void TList::InsertBefore (T * node, T * before) { + assert(!((size_t) node & 1)); + assert(!GetLinkFromNode(node)->IsLinked()); + GetLinkFromNode(node)->InsertBefore( + node, + before ? GetLinkFromNode(before) : &m_link + ); +} + +//============================================================================= +template +void TList::InsertAfter (T * node, T * after) { + assert(!((size_t) node & 1)); + assert(!GetLinkFromNode(node)->IsLinked()); + GetLinkFromNode(node)->InsertAfter( + node, + after ? GetLinkFromNode(after) : &m_link + ); +} + +//============================================================================= +template +TLink * TList::GetLinkFromNode (const T * node) const { + assert(m_offset != (size_t) -1); + return (TLink *) ((size_t) node + m_offset); +} + +/****************************************************************************** +* +* TListDeclare - declare a list with a known link offset +* +***/ + +//============================================================================= +template +class TListDeclare : public TList { +public: + TListDeclare (); +}; + +//============================================================================= +template +TListDeclare::TListDeclare () : TList(offset) +{} + + +//=================================== +// MIT License +// +// Copyright (c) 2010 by Patrick Wyatt +// +// 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. +//=================================== \ No newline at end of file diff --git a/DSMeshConvert/main.cpp b/DSMeshConvert/main.cpp index f21ab1e..1e886a8 100644 --- a/DSMeshConvert/main.cpp +++ b/DSMeshConvert/main.cpp @@ -14,7 +14,8 @@ //#define NVTRISTRIP // buggy?!! //#define CETS_PTERDIMAN // http://www.codercorner.com/Strips.htm // memory corruption... -#define ACTC // http://plunk.org/~grantham/public/actc/ +//#define ACTC // http://plunk.org/~grantham/public/actc/ +#define MULTIPATH // "Multi-Path Algorithm for Triangle Strips" implementation #include "NvTriStrip.h" #include "cets-pterdiman/Striper.h" #include "ac/tc.h" @@ -22,6 +23,11 @@ #include "stripping.h" #include "types.h" +#include + +#include "List.h" + +#include "opengl.h" struct Box { @@ -76,6 +82,8 @@ void PushValue(std::vector& list, u32& cmdOffset, u32& cmdIndex, u32 cmd, u } } +void MultiPathStripper(const std::vector& indices, std::vector& stripLengths, std::vector& stripIndices); + int Convert(const char* input, const char* output) { #ifdef NVTRISTRIP @@ -141,6 +149,8 @@ int Convert(const char* input, const char* output) return 3; } + InitOpenGL(); + //std::vector indices; //for ( u32 i = 0 ; i < mesh->mNumFaces ; i++ ) //{ @@ -229,6 +239,29 @@ int Convert(const char* input, const char* output) stripLengths.push_back(len); } actcEndOutput(tc); +#endif +#ifdef MULTIPATH + std::vector indices; + u32 nbIndices = mesh->mNumFaces * 3; + indices.reserve(nbIndices); + for ( u32 i = 0 ; i < nbIndices / 3 ; i++ ) + { + indices.push_back(mesh->mFaces[i].mIndices[0]); + indices.push_back(mesh->mFaces[i].mIndices[1]); + indices.push_back(mesh->mFaces[i].mIndices[2]); + } + u32 nbStrips = 0; + std::vector stripLengths; + std::vector stripIndices; + extern float* vertices; + vertices = new float[mesh->mNumVertices * 3]; + for ( u32 i = 0 ; i < mesh->mNumVertices ; i++ ) + { + vertices[i * 3 + 0] = mesh->mVertices[i].x; + vertices[i * 3 + 1] = mesh->mVertices[i].y; + vertices[i * 3 + 2] = mesh->mVertices[i].z; + } + MultiPathStripper(indices, stripLengths, stripIndices); #endif printf("%d strips generated for %d triangles\n", nbStrips, mesh->mNumFaces); @@ -304,6 +337,10 @@ int Convert(const char* input, const char* output) PushValue(list, command, cmdindex, 0x40, 2); // begin triangle strip //printf("begin strip\n"); #endif +#ifdef MULTIPATH + u32 idxLen = stripLengths[i]; + PushValue(list, command, cmdindex, 0x40, 2); // start strip +#endif for ( u32 j = idxLen ; j > 0 ; j--, idx++ ) { diff --git a/DSMeshConvert/multipath.cpp b/DSMeshConvert/multipath.cpp new file mode 100644 index 0000000..c791e0d --- /dev/null +++ b/DSMeshConvert/multipath.cpp @@ -0,0 +1,662 @@ +#include "types.h" +#include +#include +#include +#include "List.h" +#include "opengl.h" + +#define MAX_STRIPS 1024 + +float* vertices; + +struct Face +{ + u32 idx[3]; + + u32 connectivity; + Face* adj[3]; + + u32 dualConnectivity; + bool dual[3]; + + enum Set + { + UNCONNECTED, + CONNECTED, + FULLY_CONNECTED + }; + Set set; + + s32 strip; + + LIST_LINK(Face) dualLink; + LIST_LINK(Face) stripLink; + + Face() : connectivity(0), dualConnectivity(0), set(UNCONNECTED), strip(-1) { adj[0] = adj[1] = adj[2] = 0; dual[0] = dual[1] = dual[2] = false; } + + void RemoveFromDual(Face* f) + { + assert(dualConnectivity > 0); + dualConnectivity--; + for ( u32 i = 0 ; i < connectivity ; i++ ) + { + if ( adj[i] == f ) + { + dual[i] = false; + return; + } + } + assert(0 && "face is not adjacent in dual graph..."); + } + + bool IsAdjacent(Face* f) + { + for ( u32 i = 0 ; i < connectivity ; i++ ) + if ( dual[i] && f == adj[i] ) + return true; + return false; + } +}; + +void Disconnect(Face* f, Face* except, LIST_DECLARE(Face, dualLink) unconnected[4], LIST_DECLARE(Face, dualLink) connected[2]) +{ + for ( u32 i = 0 ; i < f->connectivity ; i++ ) + { + if ( f->dual[i] && f->adj[i] != except ) + { + f->adj[i]->RemoveFromDual(f); + f->adj[i]->dualLink.Unlink(); + if ( f->adj[i]->set == Face::UNCONNECTED ) + { + unconnected[f->adj[i]->dualConnectivity].InsertTail(f->adj[i]); + } + else if ( f->adj[i]->set == Face::CONNECTED ) + { + u32 c = f->adj[i]->dualConnectivity - 1; + assert(c < 2); + connected[c].InsertTail(f->adj[i]); + } + f->dual[i] = false; + } + } + f->dualConnectivity = 0; + f->set = Face::FULLY_CONNECTED; +} + +template +u32 Length(T& unconnected) +{ + u32 len = 0; + Face* f = unconnected.Head(); + while ( f != 0 ) + { + len++; + f = f->dualLink.Next(); + } + return len; +} + +bool ShareEdge(Face* a, Face* b) +{ + int edge0 = -1, edge1 = -1; + for ( u32 i = 0 ; i < 3 ; i++ ) + { + for ( u32 j = 0 ; j < 3 ; j++ ) + { + if ( a->idx[j] == b->idx[i] ) + { + if ( edge0 == -1 && edge0 != a->idx[j] ) + edge0 = a->idx[j]; + else if ( edge1 == -1 && edge1 != a->idx[j] ) + edge1 = a->idx[j]; + } + } + } + return edge0 != -1 && edge1 != -1; +} + +void DrawFace(const Face& face) +{ + glVertex3f(vertices[face.idx[0] * 3], vertices[face.idx[0] * 3 + 1], vertices[face.idx[0] * 3 + 2]); + glVertex3f(vertices[face.idx[1] * 3], vertices[face.idx[1] * 3 + 1], vertices[face.idx[1] * 3 + 2]); + glVertex3f(vertices[face.idx[2] * 3], vertices[face.idx[2] * 3 + 1], vertices[face.idx[2] * 3 + 2]); +} + +void DrawBorder(const Face& face) +{ + float v[3][3]; + for ( u32 i = 0 ; i < 3 ; i++ ) + { + v[i][0] = vertices[face.idx[i] * 3]; + v[i][1] = vertices[face.idx[i] * 3 + 1]; + v[i][2] = vertices[face.idx[i] * 3 + 2]; + } + + float cx = (v[0][0] + v[1][0] + v[2][0]) / 3.f; + float cy = (v[0][1] + v[1][1] + v[2][1]) / 3.f; + float cz = (v[0][2] + v[1][2] + v[2][2]) / 3.f; + + glBegin(GL_TRIANGLES); + for ( u32 i = 0 ; i < 3 ; i++ ) + { + float x, y, z; + x = v[i][0]; + y = v[i][1]; + z = v[i][2]; + + x = (x - cx) * .9f; + y = (y - cy) * .9f; + z = (z - cz) * .9f; + + x += cx; + y += cy; + z += cz; + + glVertex3f(x, y, z); + } + glEnd(); +} + +void DrawFaces(const Face* faces, u32 nbTris) +{ + glBegin(GL_TRIANGLES); + for ( u32 i = 0 ; i < nbTris ; i++ ) + { + float c = faces[i].dualConnectivity / 3.f; + glColor3f(c, c, c); + DrawFace(faces[i]); + } + glEnd(); +} + +void DrawEdge(Face* a, Face* b) +{ + float ax = (vertices[a->idx[0] * 3] + vertices[a->idx[1] * 3] + vertices[a->idx[2] * 3]) / 3.f; + float ay = (vertices[a->idx[0] * 3 + 1] + vertices[a->idx[1] * 3 + 1] + vertices[a->idx[2] * 3 + 1]) / 3.f; + float az = (vertices[a->idx[0] * 3 + 2] + vertices[a->idx[1] * 3 + 2] + vertices[a->idx[2] * 3 + 2]) / 3.f; + float bx = (vertices[b->idx[0] * 3] + vertices[b->idx[1] * 3] + vertices[b->idx[2] * 3]) / 3.f; + float by = (vertices[b->idx[0] * 3 + 1] + vertices[b->idx[1] * 3 + 1] + vertices[b->idx[2] * 3 + 1]) / 3.f; + float bz = (vertices[b->idx[0] * 3 + 2] + vertices[b->idx[1] * 3 + 2] + vertices[b->idx[2] * 3 + 2]) / 3.f; + + glBegin(GL_LINES); + glVertex3f(ax, ay, az); + glVertex3f(bx, by, bz); + glEnd(); +} + +void DrawStrip(LIST_DECLARE(Face, stripLink)& strip, u32 color) +{ + if ( strip.Head() == 0 ) + return; + + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glBegin(GL_TRIANGLES); + Face* f = strip.Head(); + while ( f ) + { + ColorOpenGL(color, .5f); + DrawFace(*f); + f = f->stripLink.Next(); + } + glEnd(); + + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + ColorOpenGL(1); + DrawBorder(*strip.Head()); + ColorOpenGL(0); + DrawBorder(*strip.Tail()); +} + +void DrawAdjacency(Face* f) +{ + for ( u32 i = 0 ; i < f->connectivity ; i++ ) + { + if ( f->dual[i] ) + { + DrawEdge(f, f->adj[i]); + } + } +} + +void ShowDebug(Face* faces, u32 nbTris, Face* f0, Face* f1, LIST_DECLARE(Face, stripLink) strips[MAX_STRIPS], u32 nbStrips) +{ + while ( EndSceneOpenGL() ) + { + BeginSceneOpenGL(); + + for ( u32 i = 0 ; i < nbStrips ; i++ ) + { + DrawStrip(strips[i], i); + } + + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + + glLineStipple(4, 0xAAAA); + glEnable(GL_LINE_STIPPLE); + glLineWidth(1.f); + DrawFaces(faces, nbTris); + + if ( f0 && f1 ) + { + glColor3f(1.f, 0.f, 0.f); + glDisable(GL_LINE_STIPPLE); + glLineWidth(5.f); + DrawEdge(f0, f1); + + glLineWidth(2.f); + glColor3f(0.f, 1.f, 0.f); + DrawAdjacency(f0); + glColor3f(0.f, 0.f, 1.f); + DrawAdjacency(f1); + } + } +} + +void MultiPathStripper(const std::vector& indices, std::vector& stripLengths, std::vector& stripIndices) +{ + u32 nbTris = indices.size() / 3; + + // build dual graph + Face* faces = new Face[nbTris]; + u32 nbEdges = 0; + const u32 *idx = &indices[0]; + std::map, Face*> edges; + for ( u32 i = 0 ; i < nbTris ; i++ ) + { + Face& f = faces[i]; + f.idx[0] = *idx++; + f.idx[1] = *idx++; + f.idx[2] = *idx++; + + for ( u32 v = 0 ; v < 3 ; v++ ) + { + std::pair edge(f.idx[v], f.idx[(v + 1) % 3]); + if ( edge.first > edge.second ) + std::swap(edge.first, edge.second); + auto iter = edges.find(edge); + if ( iter == edges.end() ) + { + edges[edge] = &f; + } + else + { + if ( iter->second ) + { + f.adj[f.connectivity] = iter->second; + f.dual[f.connectivity] = true; + f.connectivity++; + f.dualConnectivity++; + iter->second->adj[iter->second->connectivity] = &f; + iter->second->dual[iter->second->connectivity] = true; + iter->second->connectivity++; + iter->second->dualConnectivity++; + + assert(iter->second->connectivity <= 3); + assert(f.connectivity <= 3); + + iter->second = 0; + nbEdges++; + } + else + { + fprintf(stderr, "degenerate at edge(%d, %d)\n", edge.first, edge.second); + } + } + } + } + + // classify + LIST_DECLARE(Face, dualLink) unconnected[4]; + LIST_DECLARE(Face, dualLink) connected[2]; + + for ( u32 i = 0 ; i < nbTris ; i++ ) + unconnected[faces[i].connectivity].InsertTail(&faces[i]); + + assert(connected[0].Empty()); + assert(connected[1].Empty()); + + u32 nbStrips = 0; + LIST_DECLARE(Face, stripLink) strips[MAX_STRIPS]; + + // stripify + for ( u32 e = 0 ; e < nbEdges ; e++ ) + { +#ifdef _DEBUG + u32 realNbStrips = 0; + for ( u32 i = 0 ; i < nbStrips ; i++ ) + if ( !strips[i].Empty() ) + realNbStrips++; + + printf("[%d] strips:%d(%d) U0:%d U1:%d C1:%d U2:%d C2:%d U3:%d\n", + e, realNbStrips, nbStrips, + Length(unconnected[0]), Length(unconnected[1]), Length(connected[0]), + Length(unconnected[2]), Length(connected[1]), Length(unconnected[3])); +#endif + Face* face[2] = { 0, 0 }; + if ( !unconnected[1].Empty() ) + { + face[0] = unconnected[1].Tail(); + assert(face[0]->dualConnectivity == 1); + } + else if ( !connected[0].Empty() ) + { + face[0] = connected[0].Tail(); + assert(face[0]->dualConnectivity == 1); + } + else if ( !unconnected[2].Empty() ) + { + face[0] = unconnected[2].Tail(); + assert(face[0]->dualConnectivity == 2); + } + else if ( !connected[1].Empty() ) + { + face[0] = connected[1].Tail(); + assert(face[0]->dualConnectivity == 2); + } + else if ( !unconnected[3].Empty() ) + { + face[0] = unconnected[3].Tail(); + assert(face[0]->dualConnectivity == 3); + } + else + { + break; + } + assert(face[0]); + assert(face[0]->dualConnectivity > 0); + face[0]->dualLink.Unlink(); + + for ( u32 i = 0 ; face[1] == 0 && i < face[0]->connectivity ; i++ ) + { + if ( face[0]->dual[i] ) + face[1] = face[0]->adj[i]; + } + assert(face[1]); + face[1]->dualLink.Unlink(); + + ShowDebug(faces, nbTris, face[0], face[1], strips, nbStrips); + + assert(face[0]->set != Face::FULLY_CONNECTED); + assert(face[1]->set != Face::FULLY_CONNECTED); + + // attempt to concatenate with an existing strip + s32 stripID = -1; + if ( face[0]->set == Face::CONNECTED && face[1]->set == Face::CONNECTED ) + { + if ( face[0]->strip != face[1]->strip ) + { + LIST_DECLARE(Face, stripLink)& strip0 = strips[face[0]->strip]; + LIST_DECLARE(Face, stripLink)& strip1 = strips[face[1]->strip]; + Face* strip0head = strip0.Head(); + Face* strip0tail = strip0.Tail(); + Face* strip1head = strip1.Head(); + Face* strip1tail = strip1.Tail(); + if ( strip0.Head()->IsAdjacent(strip1.Tail()) ) + { + Face* f = strip1.Tail(); + while ( f ) + { + Face* next = f->stripLink.Prev(); + f->stripLink.Unlink(); + assert(ShareEdge(f, strip0.Head())); + strip0.InsertHead(f); + f->strip = face[0]->strip; + f = next; + } + stripID = face[0]->strip; + } + else if ( strip1.Head()->IsAdjacent(strip0.Tail()) ) + { + Face* f = strip0.Tail(); + while ( f ) + { + Face* next = f->stripLink.Prev(); + f->stripLink.Unlink(); + assert(ShareEdge(f, strip1.Head())); + strip1.InsertHead(f); + f->strip = face[1]->strip; + f = next; + } + stripID = face[1]->strip; + } + else if ( strip1.Tail()->IsAdjacent(strip0.Tail()) ) + { + Face* f = strip1.Tail(); + while ( f ) + { + Face* next = f->stripLink.Prev(); + f->stripLink.Unlink(); + assert(ShareEdge(f, strip0.Tail())); + strip0.InsertTail(f); + f->strip = strip0.Head()->strip; + f = next; + } + stripID = strip0.Head()->strip; + } + else if ( strip1.Head()->IsAdjacent(strip0.Head()) ) + { + Face* f = strip1.Head(); + while ( f ) + { + Face* next = f->stripLink.Next(); + f->stripLink.Unlink(); + assert(ShareEdge(f, strip0.Head())); + strip0.InsertHead(f); + f->strip = strip0.Tail()->strip; + f = next; + } + stripID = strip0.Head()->strip; + } + } + else + { + stripID = face[0]->strip; + } + } + else if ( face[0]->set == Face::CONNECTED ) + { + if ( face[1]->set != Face::CONNECTED ) + { + LIST_DECLARE(Face, stripLink)& strip = strips[face[0]->strip]; + if ( face[0]->stripLink.Next() == 0 ) + { + Face* tail = strip.Tail(); + assert(ShareEdge(face[1], tail)); + strip.InsertTail(face[1]); + stripID = face[0]->strip; + } + else if ( face[0]->stripLink.Prev() == 0 ) + { + assert(ShareEdge(face[1], strip.Head())); + strip.InsertHead(face[1]); + stripID = face[0]->strip; + } + } + } + else if ( face[1]->set == Face::CONNECTED ) + { + if ( face[0]->set != Face::CONNECTED ) + { + LIST_DECLARE(Face, stripLink)& strip = strips[face[1]->strip]; + if ( face[1]->stripLink.Next() == 0 ) + { + assert(ShareEdge(face[0], strip.Tail())); + strip.InsertTail(face[0]); + stripID = face[1]->strip; + } + else if ( face[1]->stripLink.Prev() == 0 ) + { + assert(ShareEdge(face[0], strip.Head())); + strip.InsertHead(face[0]); + stripID = face[1]->strip; + } + } + } + else + { + assert(face[0]->set != Face::FULLY_CONNECTED); + assert(face[1]->set != Face::FULLY_CONNECTED); + for ( u32 s = 0 ; stripID == -1 && s < nbStrips ; s++ ) + { + LIST_DECLARE(Face, stripLink)& tmp = strips[s]; + + if ( tmp.Empty() ) + continue; + + Face* head = tmp.Head(); + assert(head); + assert(ShareEdge(head, head->stripLink.Next())); + for ( u32 f = 0 ; stripID == -1 && f < head->connectivity ; f++ ) + { + if ( head->dual[f] ) + { + if ( head->adj[f] == face[0] ) + { + if ( !face[0]->stripLink.IsLinked() ) + { + assert(ShareEdge(face[0], tmp.Head())); + tmp.InsertHead(face[0]); + } + if ( !face[1]->stripLink.IsLinked() && head != face[1] && tmp.Tail() != face[1] ) + { + assert(ShareEdge(face[1], tmp.Head())); + tmp.InsertHead(face[1]); + } + stripID = s; + } + else if ( head->adj[f] == face[1] ) + { + if ( !face[1]->stripLink.IsLinked() ) + { + assert(ShareEdge(face[1], tmp.Head())); + tmp.InsertHead(face[1]); + } + if ( !face[0]->stripLink.IsLinked() && head != face[0] && tmp.Tail() != face[0] ) + { + assert(ShareEdge(face[0], tmp.Head())); + tmp.InsertHead(face[0]); + } + stripID = s; + } + } + } + if ( stripID != -1 ) + break; + Face* tail = tmp.Tail(); + assert(tail); + assert(ShareEdge(tail, tail->stripLink.Prev())); + for ( u32 f = 0 ; stripID == -1 && f < tail->connectivity ; f++ ) + { + if ( tail->dual[f] ) + { + if ( tail->adj[f] == face[0] ) + { + if ( !face[0]->stripLink.IsLinked() ) + { + assert(ShareEdge(face[0], tmp.Tail())); + tmp.InsertTail(face[0]); + } + if ( !face[1]->stripLink.IsLinked() && tail != face[1] && tmp.Head() != face[1] ) + { + assert(ShareEdge(face[1], tmp.Tail())); + tmp.InsertTail(face[1]); + } + stripID = s; + } + else if ( tail->adj[f] == face[1] ) + { + if ( !face[1]->stripLink.IsLinked() ) + { + assert(ShareEdge(face[1], tmp.Tail())); + tmp.InsertTail(face[1]); + } + if ( !face[0]->stripLink.IsLinked() && tail != face[0] && tmp.Head() != face[0] ) + { + assert(ShareEdge(face[0], tmp.Tail())); + tmp.InsertTail(face[0]); + } + stripID = s; + } + } + } + } + if ( stripID == -1 ) + { + assert(nbStrips < MAX_STRIPS); + stripID = nbStrips; + LIST_DECLARE(Face, stripLink)* strip = &strips[stripID]; + nbStrips++; + assert(face[0] && face[1]); + assert(!face[0]->stripLink.IsLinked()); + assert(!face[1]->stripLink.IsLinked()); + assert(ShareEdge(face[0], face[1])); + strip->InsertHead(face[0]); + strip->InsertTail(face[1]); + assert(strip->Head()); + assert(strip->Tail()); + } + } + + if ( stripID != -1 ) + { + for ( u32 i = 0 ; i < 2 ; i++ ) + { + if ( face[i]->set != Face::UNCONNECTED ) + { + assert(face[i]->strip == stripID); + } + else + { + assert(face[i]->strip == -1); + face[i]->strip = stripID; + } + } + } + + for ( u32 f = 0 ; f < 2 ; f++ ) + { + if ( face[f]->strip != -1 ) + { + if ( face[f]->set == Face::UNCONNECTED ) + { + face[f]->set = Face::CONNECTED; + if ( face[f]->dualConnectivity >= 2 ) + { + u32 c = face[f]->dualConnectivity - 2; + connected[c].InsertTail(face[f]); + } + face[f]->RemoveFromDual(face[(f + 1) % 2]); + } + else if ( face[f]->set == Face::CONNECTED ) + { + Disconnect(face[f], face[(f + 1) % 2], unconnected, connected); + } + else + { + assert(0 && "disconnecting a fully connected node !"); + } + } + } + } + + assert(unconnected[1].Empty()); + assert(unconnected[2].Empty()); + assert(unconnected[3].Empty()); + assert(connected[0].Empty()); + assert(connected[1].Empty()); + + while ( !unconnected[0].Empty() ) + { + Face* f = unconnected[0].Tail(); + f->dualLink.Unlink(); + + assert(nbStrips < MAX_STRIPS); + LIST_DECLARE(Face, stripLink)& strip = strips[nbStrips]; + nbStrips++; + strip.InsertHead(f); + } + + ShowDebug(faces, nbTris, 0, 0, strips, nbStrips); + + delete[] faces; +} \ No newline at end of file diff --git a/DSMeshConvert/opengl.cpp b/DSMeshConvert/opengl.cpp new file mode 100644 index 0000000..cc9b380 --- /dev/null +++ b/DSMeshConvert/opengl.cpp @@ -0,0 +1,218 @@ +#define WIN32_LEAN_AND_MEAN +#include +#include +#include "types.h" +#include +#include +#include +#include + +float minf(float a, float b) +{ + return a < b ? a : b; +} + +float maxf(float a, float b) +{ + return a > b ? a : b; +} + +HGLRC glContext; +HDC dcContext; +HWND window; +s32 mouseX = 0, mouseY = 0; +bool mousedown = false; +float rotatePhi = 0.f; +float rotateTheta = 0.f; +float dist = 4.f; +float h = .5f; + +static LRESULT CALLBACK WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch ( message ) + { + case WM_PAINT: + { + PAINTSTRUCT ps; + BeginPaint(hWnd, &ps); + EndPaint(hWnd, &ps); + return 0; + } + + case WM_DESTROY: + { + PostQuitMessage(0); + return 0; + } + + default: + { + return DefWindowProc(hWnd, message, wParam, lParam); + } + } +} + +void InitOpenGL() +{ + HINSTANCE instance = GetModuleHandle(0); + WNDCLASSEX winClass = + { + sizeof(WNDCLASSEX), // cbSize + CS_HREDRAW | CS_VREDRAW | CS_OWNDC, // style + WinProc, // lpfnWndProc + 0, // cbClsExtra + 0, // cbWndExtra + instance, // hInstance + 0, // hIcon + 0, // hCursor + (HBRUSH)(COLOR_WINDOW + 1), // hbrBackground + 0, // lpszMenuName + "OpenGL", // lpszClassName + 0 // hIconSm + }; + RegisterClassEx(&winClass); + RECT rect = {0, 0, 640, 480}; + u32 style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; + u32 styleEx = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; + AdjustWindowRectEx(&rect, style, FALSE, styleEx); + window = CreateWindow("OpenGL", "OpenGL", style, CW_USEDEFAULT, CW_USEDEFAULT, + rect.right - rect.left, rect.bottom - rect.top, 0, 0, instance, 0); + PIXELFORMATDESCRIPTOR pfd; + ZeroMemory(&pfd, sizeof(pfd)); + pfd.nSize = sizeof(pfd); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 32; + pfd.cDepthBits = 24; + pfd.cStencilBits = 8; + pfd.iLayerType = PFD_MAIN_PLANE; + + dcContext = GetDC(window); + int pixel = ChoosePixelFormat(dcContext, &pfd); + + SetPixelFormat(dcContext, pixel, &pfd); + + glContext = wglCreateContext(dcContext); + wglMakeCurrent(dcContext, glContext); + + ShowWindow(window, 1); + SetForegroundWindow(window); + + glClearColor(208.f/255.f, 192.f/255.f, 122.f/255.f, 0.f); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(45.f, 4./3., .1f, 1000.f); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + + //glEnable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} + +void DestroyOpenGL() +{ + // don't care o// +} + +void BeginSceneOpenGL() +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + gluLookAt(cosf(rotatePhi) * cosf(rotateTheta) * dist, + sinf(rotateTheta) * dist + h, + sinf(rotatePhi) * cosf(rotateTheta) * dist, + 0.f, h, 0.f, + 0., 1., 0.); +} + +bool EndSceneOpenGL() +{ + MSG msg; + while ( PeekMessage(&msg, 0, 0, 0, PM_REMOVE) ) + { + switch ( msg.message ) + { + case WM_QUIT: + exit(0); + + case WM_LBUTTONDOWN: + mousedown = true; + break; + + case WM_LBUTTONUP: + mousedown = false; + break; + + case WM_KEYDOWN: + if ( msg.wParam == 'S' ) + return false; + if ( msg.wParam == VK_ESCAPE ) + exit(0); + if ( msg.wParam == VK_DOWN ) + dist = maxf(dist + 0.2f, 1.f); + if ( msg.wParam == VK_UP ) + dist = minf(dist - 0.2f, 10.f); + if ( msg.wParam == 'Y' ) + h = maxf(h + 0.02f, -5.f); + if ( msg.wParam == 'H' ) + h = minf(h - 0.02f, 5.f); + break; + + case WM_MOUSEMOVE: + { + int x = GET_X_LPARAM(msg.lParam); + int y = GET_Y_LPARAM(msg.lParam); + if ( mousedown ) + { + rotatePhi += (x - mouseX) * .005f; + rotateTheta += (y - mouseY) * .005f; + } + mouseX = x; + mouseY = y; + break; + } + } + + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + SwapBuffers(dcContext); + + return true; +} + +void ColorOpenGL(u32 idx, float alpha) +{ + // C64 color palette \o/ + // from: http://www.pepto.de/projects/colorvic/ + static const float palette[][3] = + { + { 0.000f, 0.000f, 0.000f }, // black + { 254.999f, 254.999f, 254.999f }, // white + { 103.681f, 55.445f, 43.038f }, // red + { 111.932f, 163.520f, 177.928f }, // cyan + { 111.399f, 60.720f, 133.643f }, // purple + { 88.102f, 140.581f, 67.050f }, // green + { 52.769f, 40.296f, 121.446f }, // blue + { 183.892f, 198.676f, 110.585f }, // yellow + { 111.399f, 79.245f, 37.169f }, // orange + { 66.932f, 57.383f, 0.000f }, // brown + { 153.690f, 102.553f, 89.111f }, // light red + { 67.999f, 67.999f, 67.999f }, // dark grey + { 107.797f, 107.797f, 107.797f }, // grey + { 154.244f, 209.771f, 131.584f }, // light green + { 107.797f, 94.106f, 180.927f }, // light blue + { 149.480f, 149.480f, 149.480f }, // light grey + }; + + idx %= _countof(palette); + glColor4f(palette[idx][0]/255.f, palette[idx][1]/255.f, palette[idx][2]/255.f, alpha); +} \ No newline at end of file diff --git a/DSMeshConvert/opengl.h b/DSMeshConvert/opengl.h new file mode 100644 index 0000000..4505868 --- /dev/null +++ b/DSMeshConvert/opengl.h @@ -0,0 +1,15 @@ +#pragma once +#ifndef _OPENGL_H_ +#define _OPENGL_H_ + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +void InitOpenGL(); +bool EndSceneOpenGL(); +void BeginSceneOpenGL(); +void ColorOpenGL(u32 idx, float alpha = 1.f); + +#endif // _OPENGL_H_ \ No newline at end of file