Compare commits

4 Commits

Author SHA1 Message Date
daniel borges
de74b97937 - 2012-09-18 19:18:57 +02:00
daniel borges
591239b339 - added free list of strips
- tried a few weightings for the selection of adjacent faces
2012-09-17 23:18:03 +02:00
daniel borges
194673b5dd it's working ! \o/
suzanne in 42 strips
2012-09-17 21:08:40 +02:00
daniel borges
a4ec1322fe WIP multipath stripping 2012-09-17 09:08:05 +02:00
7 changed files with 1567 additions and 4 deletions

View File

@@ -59,7 +59,7 @@
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
</ClCompile>
<Link>
<AdditionalDependencies>assimp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>assimp.lib;%(AdditionalDependencies);OpenGL32.lib;glu32.lib</AdditionalDependencies>
<AdditionalLibraryDirectories>assimp--1.1.700-sdk\lib\assimp_debug-dll_win32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>true</GenerateDebugInformation>
<TargetMachine>MachineX86</TargetMachine>
@@ -86,6 +86,7 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="multipath.cpp" />
<ClCompile Include="NvTriStrip\NvTriStrip.cpp" />
<ClCompile Include="NvTriStrip\NvTriStripObjects.cpp" />
<ClCompile Include="cets-pterdiman\Adjacency.cpp" />
@@ -95,9 +96,11 @@
<ClCompile Include="cets-pterdiman\Strips.cpp" />
<ClCompile Include="ac\tc.c" />
<ClCompile Include="main.cpp" />
<ClCompile Include="opengl.cpp" />
<ClCompile Include="stripping.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="List.h" />
<ClInclude Include="NvTriStrip\NvTriStrip.h" />
<ClInclude Include="NvTriStrip\NvTriStripObjects.h" />
<ClInclude Include="NvTriStrip\VertexCache.h" />
@@ -106,6 +109,7 @@
<ClInclude Include="cets-pterdiman\RevisitedRadix.h" />
<ClInclude Include="cets-pterdiman\Striper.h" />
<ClInclude Include="ac\tc.h" />
<ClInclude Include="opengl.h" />
<ClInclude Include="stripping.h" />
<ClInclude Include="types.h" />
</ItemGroup>

View File

@@ -38,6 +38,8 @@
</ClCompile>
<ClCompile Include="main.cpp" />
<ClCompile Include="stripping.cpp" />
<ClCompile Include="multipath.cpp" />
<ClCompile Include="opengl.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="NvTriStrip\NvTriStrip.h">
@@ -66,5 +68,7 @@
</ClInclude>
<ClInclude Include="stripping.h" />
<ClInclude Include="types.h" />
<ClInclude Include="List.h" />
<ClInclude Include="opengl.h" />
</ItemGroup>
</Project>

491
DSMeshConvert/List.h Normal file
View File

@@ -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<T, offsetof(T, link)>
// Define a field within a structure that will be used to link it into a list
#define LIST_LINK(T) TLink<T>
// Define a pointer to a list
#define LIST_PTR(T) TList<T> *
/******************************************************************************
*
* TLink
*
***/
//=============================================================================
template<class T>
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<T>, THash<T>,
// and (eventually) many other structures.
TLink (size_t offset);
void SetOffset (size_t offset);
TLink<T> * NextLink ();
TLink<T> * PrevLink ();
void InsertBefore (T * node, TLink<T> * nextLink);
void InsertAfter (T * node, TLink<T> * prevLink);
private:
T * m_nextNode; // pointer to the next >object<
TLink<T> * m_prevLink; // pointer to the previous >link field<
void RemoveFromList ();
// Hide copy-constructor and assignment operator
TLink (const TLink &);
TLink & operator= (const TLink &);
};
//=============================================================================
template<class T>
TLink<T>::~TLink () {
RemoveFromList();
}
//=============================================================================
template<class T>
TLink<T>::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<class T>
TLink<T>::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<class T>
void TLink<T>::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<class T>
TLink<T> * TLink<T>::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<T> *) (((size_t) m_nextNode & ~1) + offset);
}
//=============================================================================
template<class T>
void TLink<T>::RemoveFromList () {
NextLink()->m_prevLink = m_prevLink;
m_prevLink->m_nextNode = m_nextNode;
}
//=============================================================================
template<class T>
void TLink<T>::InsertBefore (T * node, TLink<T> * nextLink) {
RemoveFromList();
m_prevLink = nextLink->m_prevLink;
m_nextNode = m_prevLink->m_nextNode;
nextLink->m_prevLink->m_nextNode = node;
nextLink->m_prevLink = this;
}
//=============================================================================
template<class T>
void TLink<T>::InsertAfter (T * node, TLink<T> * prevLink) {
RemoveFromList();
m_prevLink = prevLink;
m_nextNode = prevLink->m_nextNode;
prevLink->NextLink()->m_prevLink = this;
prevLink->m_nextNode = node;
}
//=============================================================================
template<class T>
bool TLink<T>::IsLinked () const {
return m_prevLink != this;
}
//=============================================================================
template<class T>
void TLink<T>::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<class T>
TLink<T> * TLink<T>::PrevLink () {
return m_prevLink;
}
//=============================================================================
template<class T>
T * TLink<T>::Prev () {
T * prevNode = m_prevLink->m_prevLink->m_nextNode;
if ((size_t) prevNode & 1)
return NULL;
return prevNode;
}
//=============================================================================
template<class T>
const T * TLink<T>::Prev () const {
const T * prevNode = m_prevLink->m_prevLink->m_nextNode;
if ((size_t) prevNode & 1)
return NULL;
return prevNode;
}
//=============================================================================
template<class T>
T * TLink<T>::Next () {
if ((size_t) m_nextNode & 1)
return NULL;
return m_nextNode;
}
//=============================================================================
template<class T>
const T * TLink<T>::Next () const {
if ((size_t) m_nextNode & 1)
return NULL;
return m_nextNode;
}
/******************************************************************************
*
* TList
*
***/
//=============================================================================
template<class T>
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<T> m_link;
size_t m_offset;
TList (size_t offset);
TLink<T> * GetLinkFromNode (const T * node) const;
template<class T, size_t offset> friend class TListDeclare;
// Hide copy-constructor and assignment operator
TList (const TList &);
TList & operator= (const TList &);
};
//=============================================================================
template<class T>
TList<T>::~TList () {
UnlinkAll();
}
//=============================================================================
template<class T>
TList<T>::TList () :
m_link(),
m_offset((size_t) -1)
{}
//=============================================================================
template<class T>
TList<T>::TList (size_t offset) :
m_link(offset),
m_offset(offset)
{}
//=============================================================================
template<class T>
bool TList<T>::Empty () const {
return m_link.Next() == NULL;
}
//=============================================================================
template<class T>
void TList<T>::UnlinkAll () {
for (;;) {
TLink<T> * link = m_link.PrevLink();
if (link == &m_link)
break;
link->Unlink();
}
}
//=============================================================================
template<class T>
void TList<T>::DeleteAll () {
while (T * node = m_link.Next())
delete node;
}
//=============================================================================
template<class T>
T * TList<T>::Head () {
return m_link.Next();
}
//=============================================================================
template<class T>
T * TList<T>::Tail () {
return m_link.Prev();
}
//=============================================================================
template<class T>
const T * TList<T>::Head () const {
return m_link.Next();
}
//=============================================================================
template<class T>
const T * TList<T>::Tail () const {
return m_link.Prev();
}
//=============================================================================
template<class T>
T * TList<T>::Prev (T * node) {
return GetLinkFromNode(node)->Prev();
}
//=============================================================================
template<class T>
const T * TList<T>::Prev (const T * node) const {
return GetLinkFromNode(node)->Prev();
}
//=============================================================================
template<class T>
T * TList<T>::Next (T * node) {
return GetLinkFromNode(node)->Next();
}
//=============================================================================
template<class T>
const T * TList<T>::Next (const T * node) const {
return GetLinkFromNode(node)->Next();
}
//=============================================================================
template<class T>
void TList<T>::InsertHead (T * node) {
InsertAfter(node, NULL);
}
//=============================================================================
template<class T>
void TList<T>::InsertTail (T * node) {
InsertBefore(node, NULL);
}
//=============================================================================
template<class T>
void TList<T>::InsertBefore (T * node, T * before) {
assert(!((size_t) node & 1));
assert(!GetLinkFromNode(node)->IsLinked());
GetLinkFromNode(node)->InsertBefore(
node,
before ? GetLinkFromNode(before) : &m_link
);
}
//=============================================================================
template<class T>
void TList<T>::InsertAfter (T * node, T * after) {
assert(!((size_t) node & 1));
assert(!GetLinkFromNode(node)->IsLinked());
GetLinkFromNode(node)->InsertAfter(
node,
after ? GetLinkFromNode(after) : &m_link
);
}
//=============================================================================
template<class T>
TLink<T> * TList<T>::GetLinkFromNode (const T * node) const {
assert(m_offset != (size_t) -1);
return (TLink<T> *) ((size_t) node + m_offset);
}
/******************************************************************************
*
* TListDeclare - declare a list with a known link offset
*
***/
//=============================================================================
template<class T, size_t offset>
class TListDeclare : public TList<T> {
public:
TListDeclare ();
};
//=============================================================================
template<class T, size_t offset>
TListDeclare<T, offset>::TListDeclare () : TList<T>(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.
//===================================

View File

@@ -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 <assert.h>
#include "List.h"
#include "opengl.h"
struct Box
{
@@ -76,6 +82,8 @@ void PushValue(std::vector<u32>& list, u32& cmdOffset, u32& cmdIndex, u32 cmd, u
}
}
void MultiPathStripper(const std::vector<u32>& indices, std::vector<u32>& stripLengths, std::vector<u32>& stripIndices);
int Convert(const char* input, const char* output)
{
#ifdef NVTRISTRIP
@@ -105,6 +113,8 @@ int Convert(const char* input, const char* output)
| aiComponent_CAMERAS
| aiComponent_MATERIALS);
printf("Loading %s...", input);
// Import file
const aiScene* scene = importer.ReadFile(input,
aiProcess_FixInfacingNormals
@@ -116,10 +126,12 @@ int Convert(const char* input, const char* output)
| aiProcess_FindDegenerates
| aiProcess_SortByPType
| aiProcess_FindInstances
| aiProcess_OptimizeMeshes
| aiProcess_ImproveCacheLocality
//| aiProcess_OptimizeMeshes
//| aiProcess_ImproveCacheLocality
| aiProcess_RemoveComponent);
puts(" done");
if ( scene == 0 )
{
fprintf(stderr, "Could not import %s\n", input);
@@ -141,6 +153,11 @@ int Convert(const char* input, const char* output)
return 3;
}
printf("Initializing OpenGL\n");
InitOpenGL();
printf("Stripping...\n");
//std::vector<u32> indices;
//for ( u32 i = 0 ; i < mesh->mNumFaces ; i++ )
//{
@@ -229,9 +246,34 @@ int Convert(const char* input, const char* output)
stripLengths.push_back(len);
}
actcEndOutput(tc);
#endif
#ifdef MULTIPATH
std::vector<u32> 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<u32> stripLengths;
std::vector<u32> 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);
puts("Scaling...");
// TODO: AABB => OBB, for higher precision
Box box = ComputeBoundingBox(mesh->mVertices, mesh->mNumVertices);
aiVector3D minDS(-7.99f);
@@ -244,6 +286,8 @@ int Convert(const char* input, const char* output)
translate = translate / scale;
scale = (maxDS - minDS) / scale;
puts("Generating display list...");
// Generate display list
std::vector<u32> list;
@@ -304,6 +348,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++ )
{
@@ -353,11 +401,15 @@ int Convert(const char* input, const char* output)
}
}
printf("Exporting to %s...", output);
// Output file
FILE* f = fopen(output, "wb");
fwrite(&list[0], sizeof(list[0]), list.size(), f);
fclose(f);
puts(" done");
#ifdef NVTRISTRIP
delete[] strips;
delete[] indices;

767
DSMeshConvert/multipath.cpp Normal file
View File

@@ -0,0 +1,767 @@
#include "types.h"
#include <assert.h>
#include <vector>
#include <map>
#include "List.h"
#include "opengl.h"
#define MAX_STRIPS 16384
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;
LIST_LINK(Face) freeLink;
s32 freeStrip;
Face() : connectivity(0), dualConnectivity(0), set(UNCONNECTED), strip(-1), freeStrip(-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 )
{
if ( f->adj[i]->dualConnectivity == 0 )
{
Disconnect(f->adj[i], 0, unconnected, connected);
}
else
{
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 <typename T>
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, float alpha, bool headtail)
{
if ( strip.Head() == 0 )
return;
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glBegin(GL_TRIANGLES);
Face* f = strip.Head();
while ( f )
{
ColorOpenGL(color, alpha);
DrawFace(*f);
f = f->stripLink.Next();
}
glEnd();
if ( headtail )
{
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, .5f, TRUE);
}
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 ShowStrips(LIST_DECLARE(Face, stripLink) strips[MAX_STRIPS], u32 nbStrips)
{
while ( EndSceneOpenGL() )
{
BeginSceneOpenGL();
for ( u32 i = 0 ; i < nbStrips ; i++ )
{
DrawStrip(strips[i], i, 1.f, FALSE);
}
}
}
void MultiPathStripper(const std::vector<u32>& indices, std::vector<u32>& stripLengths, std::vector<u32>& stripIndices)
{
u32 nbTris = indices.size() / 3;
// build dual graph
Face* faces = new Face[nbTris];
u32 nbEdges = 0;
const u32 *idx = &indices[0];
std::map<std::pair<u32, u32>, 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<u32, u32> 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];
LIST_DECLARE(Face, freeLink) freeStrips;
// stripify
for ( u32 e = 0 ; e < nbEdges ; e++ )
{
#ifdef _DEBUG
//if ( e % 32 == 0 )
//{
// 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();
u32 power = 0;
for ( u32 i = 0 ; i < face[0]->connectivity ; i++ )
{
if ( face[0]->dual[i] )
{
//u32 p = 1;
//u32 p = (3 - face[0]->adj[i]->dualConnectivity) + 1 + face[0]->adj[i]->set * 10;
u32 p = face[0]->adj[i]->dualConnectivity + 1 + face[0]->adj[i]->set * 10;
//u32 p = face[0]->adj[i]->dualConnectivity + 1 + (2 - face[0]->adj[i]->set) * 10;
//u32 p = face[0]->adj[i]->strip + 10;
if ( p > power )
{
face[1] = face[0]->adj[i];
power = p;
}
}
}
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 )
{
// merge one strip into the other
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()) )
{
freeStrips.InsertHead(face[1]);
assert(face[1]->freeStrip == -1);
face[1]->freeStrip = face[1]->strip;
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()) )
{
freeStrips.InsertHead(face[0]);
assert(face[0]->freeStrip == -1);
face[0]->freeStrip = face[0]->strip;
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()) )
{
freeStrips.InsertHead(face[1]);
assert(face[1]->freeStrip == -1);
face[1]->freeStrip = face[1]->strip;
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()) )
{
freeStrips.InsertHead(face[1]);
assert(face[1]->freeStrip == -1);
face[1]->freeStrip = face[1]->strip;
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 )
{
// attach face to strip
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 )
{
// attach face to strip
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
{
// try to find a strip where both faces can be attached to
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 )
{
// create a new strip
if ( !freeStrips.Empty() )
{
stripID = freeStrips.Head()->freeStrip;
freeStrips.Head()->freeStrip = -1;
freeStrips.Head()->freeLink.Unlink();
}
else
{
assert(nbStrips < MAX_STRIPS);
stripID = nbStrips;
nbStrips++;
}
LIST_DECLARE(Face, stripLink)* strip = &strips[stripID];
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());
}
}
// tag faces
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;
}
}
}
// update dual graph
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 !");
}
}
else
{
face[f]->dualLink.Unlink();
face[f]->RemoveFromDual(face[(f + 1) % 2]);
unconnected[face[f]->dualConnectivity].InsertTail(face[f]);
}
}
}
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();
u32 stripID = -1;
if ( !freeStrips.Empty() )
{
stripID = freeStrips.Head()->freeStrip;
freeStrips.Head()->freeStrip = -1;
freeStrips.Head()->freeLink.Unlink();
}
else
{
assert(nbStrips < MAX_STRIPS);
stripID = nbStrips;
nbStrips++;
}
LIST_DECLARE(Face, stripLink)& strip = strips[stripID];
strip.InsertHead(f);
}
#ifdef _DEBUG
u32 realNbStrips = 0;
for ( u32 i = 0 ; i < nbStrips ; i++ )
if ( !strips[i].Empty() )
realNbStrips++;
printf("[end] strips:%d(%d) U0:%d U1:%d C1:%d U2:%d C2:%d U3:%d\n",
realNbStrips, nbStrips,
Length(unconnected[0]), Length(unconnected[1]), Length(connected[0]),
Length(unconnected[2]), Length(connected[1]), Length(unconnected[3]));
#endif
glEnable(GL_DEPTH_TEST);
ShowStrips(strips, nbStrips);
delete[] faces;
}

230
DSMeshConvert/opengl.cpp Normal file
View File

@@ -0,0 +1,230 @@
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <WindowsX.h>
#include "types.h"
#include <gl/GL.h>
#include <gl/GLU.h>
#include <math.h>
#include <stdlib.h>
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;
float scale = 1.f;
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.);
glBegin(GL_LINES);
glColor3f(1.f, 0.f, 0.f);
glVertex3f(0.f, 0.f, 0.f);
glVertex3f(1000.f, 0.f, 0.f);
glColor3f(0.f, 1.f, 0.f);
glVertex3f(0.f, 0.f, 0.f);
glVertex3f(0.f, 1000.f, 0.f);
glColor3f(0.f, 0.f, 1.f);
glVertex3f(0.f, 0.f, 0.f);
glVertex3f(0.f, 0.f, 1000.f);
glEnd();
glScalef(scale, scale, scale);
}
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:
switch ( msg.wParam )
{
case 'S' : return false;
case VK_ESCAPE: exit(0);
case VK_DOWN: dist = maxf(dist + .02f, 1.f); break;
case VK_UP: dist = minf(dist - 0.02f, 10.f); break;
case 'Y': h = maxf(h + .02f, -5.f); break;
case 'H': h = minf(h - .02f, 5.f); break;
case 'Z': scale *= 2.f; break;
case 'A': scale /= 2.f; break;
}
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);
}

15
DSMeshConvert/opengl.h Normal file
View File

@@ -0,0 +1,15 @@
#pragma once
#ifndef _OPENGL_H_
#define _OPENGL_H_
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <gl/GL.h>
#include <gl/GLU.h>
void InitOpenGL();
bool EndSceneOpenGL();
void BeginSceneOpenGL();
void ColorOpenGL(u32 idx, float alpha = 1.f);
#endif // _OPENGL_H_