diff --git a/examples/OpenGL/OpenGLImmediateModeSupport.cpp b/examples/OpenGL/OpenGLImmediateModeSupport.cpp index b868a907..a5ac4b78 100644 --- a/examples/OpenGL/OpenGLImmediateModeSupport.cpp +++ b/examples/OpenGL/OpenGLImmediateModeSupport.cpp @@ -6,15 +6,19 @@ using namespace PolyVox; using namespace std; -void renderRegionImmediateMode(PolyVox::IndexedSurfacePatch& isp) +void renderRegionImmediateMode(PolyVox::IndexedSurfacePatch& isp, unsigned int uLodLevel) { const vector& vecVertices = isp.getVertices(); const vector& vecIndices = isp.getIndices(); + int beginIndex = isp.m_vecLodRecords[uLodLevel].beginIndex; + int endIndex = isp.m_vecLodRecords[uLodLevel].endIndex; + glBegin(GL_TRIANGLES); - for(vector::const_iterator iterIndex = vecIndices.begin(); iterIndex != vecIndices.end(); ++iterIndex) + //for(vector::const_iterator iterIndex = vecIndices.begin(); iterIndex != vecIndices.end(); ++iterIndex) + for(int index = beginIndex; index < endIndex; ++index) { - const SurfaceVertex& vertex = vecVertices[*iterIndex]; + const SurfaceVertex& vertex = vecVertices[vecIndices[index]]; const Vector3DFloat& v3dVertexPos = vertex.getPosition(); //const Vector3DFloat v3dRegionOffset(uRegionX * g_uRegionSideLength, uRegionY * g_uRegionSideLength, uRegionZ * g_uRegionSideLength); const Vector3DFloat v3dFinalVertexPos = v3dVertexPos + static_cast(isp.m_Region.getLowerCorner()); diff --git a/examples/OpenGL/OpenGLImmediateModeSupport.h b/examples/OpenGL/OpenGLImmediateModeSupport.h index 1d5ac4d0..3004a4b8 100644 --- a/examples/OpenGL/OpenGLImmediateModeSupport.h +++ b/examples/OpenGL/OpenGLImmediateModeSupport.h @@ -5,6 +5,6 @@ #include "glew/glew.h" -void renderRegionImmediateMode(PolyVox::IndexedSurfacePatch& isp); +void renderRegionImmediateMode(PolyVox::IndexedSurfacePatch& isp, unsigned int uLodLevel); #endif //__OpenGLExample_OpenGLImmediateModeSupport_H__ diff --git a/examples/OpenGL/OpenGLVertexBufferObjectSupport.cpp b/examples/OpenGL/OpenGLVertexBufferObjectSupport.cpp index 3b1040a3..03925290 100644 --- a/examples/OpenGL/OpenGLVertexBufferObjectSupport.cpp +++ b/examples/OpenGL/OpenGLVertexBufferObjectSupport.cpp @@ -11,6 +11,9 @@ OpenGLSurfacePatch BuildOpenGLSurfacePatch(const IndexedSurfacePatch& isp) //Represents our filled in OpenGL vertex and index buffer objects. OpenGLSurfacePatch result; + //The source + result.sourceISP = &isp; + //Convienient access to the vertices and indices const vector& vecVertices = isp.getVertices(); const vector& vecIndices = isp.getIndices(); @@ -74,8 +77,10 @@ OpenGLSurfacePatch BuildOpenGLSurfacePatch(const IndexedSurfacePatch& isp) return result; } -void renderRegionVertexBufferObject(const OpenGLSurfacePatch& openGLSurfacePatch) +void renderRegionVertexBufferObject(const OpenGLSurfacePatch& openGLSurfacePatch, unsigned int uLodLevel) { + int beginIndex = openGLSurfacePatch.sourceISP->m_vecLodRecords[uLodLevel].beginIndex; + int endIndex = openGLSurfacePatch.sourceISP->m_vecLodRecords[uLodLevel].endIndex; glBindBuffer(GL_ARRAY_BUFFER, openGLSurfacePatch.vertexBuffer); glVertexPointer(3, GL_FLOAT, 36, 0); glNormalPointer(GL_FLOAT, 36, (GLvoid*)12); @@ -87,7 +92,8 @@ void renderRegionVertexBufferObject(const OpenGLSurfacePatch& openGLSurfacePatch glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_COLOR_ARRAY); - glDrawElements(GL_TRIANGLES, openGLSurfacePatch.noOfIndices, GL_UNSIGNED_INT, 0); + //glDrawElements(GL_TRIANGLES, openGLSurfacePatch.noOfIndices, GL_UNSIGNED_INT, 0); + glDrawRangeElements(GL_TRIANGLES, beginIndex, endIndex-1, endIndex - beginIndex,/* openGLSurfacePatch.noOfIndices,*/ GL_UNSIGNED_INT, 0); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); diff --git a/examples/OpenGL/OpenGLVertexBufferObjectSupport.h b/examples/OpenGL/OpenGLVertexBufferObjectSupport.h index a7881b91..664bc103 100644 --- a/examples/OpenGL/OpenGLVertexBufferObjectSupport.h +++ b/examples/OpenGL/OpenGLVertexBufferObjectSupport.h @@ -10,9 +10,10 @@ struct OpenGLSurfacePatch GLulong noOfIndices; GLuint indexBuffer; GLuint vertexBuffer; + const PolyVox::IndexedSurfacePatch* sourceISP; }; OpenGLSurfacePatch BuildOpenGLSurfacePatch(const PolyVox::IndexedSurfacePatch& isp); -void renderRegionVertexBufferObject(const OpenGLSurfacePatch& openGLSurfacePatch); +void renderRegionVertexBufferObject(const OpenGLSurfacePatch& openGLSurfacePatch, unsigned int uLodLevel); #endif //__OpenGLExample_OpenGLVertexBufferObjectSupport_H__ diff --git a/examples/OpenGL/OpenGLWidget.cpp b/examples/OpenGL/OpenGLWidget.cpp index 8d6c7ac4..9058bd1f 100644 --- a/examples/OpenGL/OpenGLWidget.cpp +++ b/examples/OpenGL/OpenGLWidget.cpp @@ -16,7 +16,7 @@ OpenGLWidget::OpenGLWidget(QWidget *parent) { m_xRotation = 0; m_yRotation = 0; - m_uRegionSideLength = 32.0f; + m_uRegionSideLength = 32; timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(update())); @@ -72,18 +72,23 @@ void OpenGLWidget::setVolume(PolyVox::Volume* volData) //isp->smooth(0.3f); //ispCurrent->generateAveragedFaceNormals(true); + if(isp->m_vecTriangleIndices.size() > 0) + { + isp->makeProgressiveMesh(); - Vector3DUint8 v3dRegPos(uRegionX,uRegionY,uRegionZ); - if(m_bUseOpenGLVertexBufferObjects) - { - OpenGLSurfacePatch openGLSurfacePatch = BuildOpenGLSurfacePatch(*(isp.get())); - m_mapOpenGLSurfacePatches.insert(make_pair(v3dRegPos, openGLSurfacePatch)); + + Vector3DUint8 v3dRegPos(uRegionX,uRegionY,uRegionZ); + if(m_bUseOpenGLVertexBufferObjects) + { + OpenGLSurfacePatch openGLSurfacePatch = BuildOpenGLSurfacePatch(*(isp.get())); + m_mapOpenGLSurfacePatches.insert(make_pair(v3dRegPos, openGLSurfacePatch)); + } + //else + //{ + m_mapIndexedSurfacePatches.insert(make_pair(v3dRegPos, isp)); + //} + //delete ispCurrent; } - else - { - m_mapIndexedSurfacePatches.insert(make_pair(v3dRegPos, isp)); - } - //delete ispCurrent; } } } @@ -161,15 +166,18 @@ void OpenGLWidget::paintGL() for(PolyVox::uint16_t uRegionX = 0; uRegionX < m_uVolumeWidthInRegions; ++uRegionX) { Vector3DUint8 v3dRegPos(uRegionX,uRegionY,uRegionZ); - if(m_bUseOpenGLVertexBufferObjects) - { - renderRegionVertexBufferObject(m_mapOpenGLSurfacePatches[v3dRegPos]); - } - else + if(m_mapIndexedSurfacePatches.find(v3dRegPos) != m_mapIndexedSurfacePatches.end()) { POLYVOX_SHARED_PTR ispCurrent = m_mapIndexedSurfacePatches[v3dRegPos]; - renderRegionImmediateMode(*ispCurrent); - + unsigned int uLodLevel = 0; //ispCurrent->m_vecLodRecords.size() - 1; + if(m_bUseOpenGLVertexBufferObjects) + { + renderRegionVertexBufferObject(m_mapOpenGLSurfacePatches[v3dRegPos], uLodLevel); + } + else + { + renderRegionImmediateMode(*ispCurrent, uLodLevel); + } } } } diff --git a/examples/OpenGL/main.cpp b/examples/OpenGL/main.cpp index 26032ce1..0effbce1 100644 --- a/examples/OpenGL/main.cpp +++ b/examples/OpenGL/main.cpp @@ -89,6 +89,7 @@ int main(int argc, char *argv[]) //createCubeInVolume(volData, Vector3DUint16(1, 1, 1), Vector3DUint16(maxPos-1, maxPos-1, midPos/4), 255); + volData.calculateSizeInChars(); cout << "Tidying memory..."; volData.tidyUpMemory(0); cout << "done." << endl; diff --git a/library/PolyVoxCore/CMakeLists.txt b/library/PolyVoxCore/CMakeLists.txt index 4240c5c9..3d72fcb8 100644 --- a/library/PolyVoxCore/CMakeLists.txt +++ b/library/PolyVoxCore/CMakeLists.txt @@ -7,9 +7,11 @@ SET(CORE_SRC_FILES source/GradientEstimators.cpp source/IndexedSurfacePatch.cpp source/Log.cpp + source/progmesh.cpp source/Region.cpp source/SurfaceExtractor.cpp source/SurfaceVertex.cpp + source/vector.cpp source/VoxelFilters.cpp ) @@ -17,12 +19,15 @@ SET(CORE_SRC_FILES SET(CORE_INC_FILES include/GradientEstimators.inl include/IndexedSurfacePatch.h + include/list.h include/Log.h include/PolyVoxForwardDeclarations.h + include/progmesh.h include/Region.h include/SurfaceExtractor.h include/SurfaceVertex.h include/Vector.h + include/vector_melax.h include/Vector.inl include/Volume.h include/Volume.inl @@ -80,6 +85,11 @@ IF(WIN32) #Install the core header files, including the ones in the PolyVoxImpl subfolder. INSTALL(DIRECTORY include DESTINATION PolyVoxCore COMPONENT development PATTERN "*.svn*" EXCLUDE) + + #On windows, we also install the debug information. It's unfortunate that we have to hard-code + #the 'Debug' part of the path, but CMake doesn't seem to provide a way around this. The best I + #found was: http://www.cmake.org/pipermail/cmake/2007-October/016924.html (and it is a bit ugly). + INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/Debug/PolyVoxCore_d.pdb DESTINATION PolyVoxCore/lib CONFIGURATIONS Debug) ELSE(WIN32) INSTALL(TARGETS PolyVoxCore RUNTIME DESTINATION bin @@ -91,4 +101,3 @@ ELSE(WIN32) #Install the core header files, including the ones in the PolyVoxImpl subfolder. INSTALL(DIRECTORY include/ DESTINATION include/PolyVoxCore COMPONENT development PATTERN "*.svn*" EXCLUDE) ENDIF(WIN32) - diff --git a/library/PolyVoxCore/include/IndexedSurfacePatch.h b/library/PolyVoxCore/include/IndexedSurfacePatch.h index 39e01b6b..49eece04 100644 --- a/library/PolyVoxCore/include/IndexedSurfacePatch.h +++ b/library/PolyVoxCore/include/IndexedSurfacePatch.h @@ -34,6 +34,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. namespace PolyVox { + class LodRecord + { + public: + int beginIndex; + int endIndex; //Let's put it just past the end STL style + }; + class POLYVOXCORE_API IndexedSurfacePatch { public: @@ -62,13 +69,22 @@ namespace PolyVox //Vector3DInt32 m_v3dRegionPosition; //FIXME - remove this? + /*void growMaterialBoundary(void); + int countMaterialBoundary(void);*/ + + void makeProgressiveMesh(void); + Region m_Region; int32_t m_iTimeStamp; + + int32_t m_iNoOfLod0Tris; public: std::vector m_vecTriangleIndices; std::vector m_vecVertices; + + std::vector m_vecLodRecords; }; } diff --git a/library/PolyVoxCore/include/list.h b/library/PolyVoxCore/include/list.h new file mode 100644 index 00000000..144dbcf8 --- /dev/null +++ b/library/PolyVoxCore/include/list.h @@ -0,0 +1,128 @@ +/* + * A generic template list class. + * Fairly typical of the list example you would + * find in any c++ book. + */ +#ifndef GENERIC_LIST_H +#define GENERIC_LIST_H + +#include +#include + +template class List { + public: + List(int s=0); + ~List(); + void allocate(int s); + void SetSize(int s); + void Pack(); + void Add(Type); + void AddUnique(Type); + int Contains(Type); + void Remove(Type); + void DelIndex(int i); + Type * element; + int num; + int array_size; + Type &operator[](int i){assert(i>=0 && i +List::List(int s){ + num=0; + array_size = 0; + element = NULL; + if(s) { + allocate(s); + } +} + +template +List::~List(){ + delete element; +} + +template +void List::allocate(int s){ + assert(s>0); + assert(s>=num); + Type *old = element; + array_size =s; + element = new Type[array_size]; + assert(element); + for(int i=0;i +void List::SetSize(int s){ + if(s==0) { if(element) delete element;} + else { allocate(s); } + num=s; +} +template +void List::Pack(){ + allocate(num); +} + +template +void List::Add(Type t){ + assert(num<=array_size); + if(num==array_size) { + allocate((array_size)?array_size *2:16); + } + //int i; + //for(i=0;i +int List::Contains(Type t){ + int i; + int count=0; + for(i=0;i +void List::AddUnique(Type t){ + if(!Contains(t)) Add(t); +} + + +template +void List::DelIndex(int i){ + assert(i +void List::Remove(Type t){ + int i; + for(i=0;i &vert, List &tri, + List &map, List &permutation ); + +#endif diff --git a/library/PolyVoxCore/include/vector_melax.h b/library/PolyVoxCore/include/vector_melax.h new file mode 100644 index 00000000..f56c10de --- /dev/null +++ b/library/PolyVoxCore/include/vector_melax.h @@ -0,0 +1,68 @@ +// +// This module contains a bunch of well understood functions +// I apologise if the conventions used here are slightly +// different than what you are used to. +// + +#ifndef GENERIC_VECTOR_H +#define GENERIC_VECTOR_H + +#include +#include + + +class VectorM { + public: + float x,y,z; + VectorM(float _x=0.0,float _y=0.0,float _z=0.0){x=_x;y=_y;z=_z;}; + operator float *() { return &x;}; + + float fBoundaryCost; +}; + +float magnitude(VectorM v); +VectorM normalize(VectorM v); + +VectorM operator+(VectorM v1,VectorM v2); +VectorM operator-(VectorM v); +VectorM operator-(VectorM v1,VectorM v2); +VectorM operator*(VectorM v1,float s) ; +VectorM operator*(float s,VectorM v1) ; +VectorM operator/(VectorM v1,float s) ; +float operator^(VectorM v1,VectorM v2); // DOT product +VectorM operator*(VectorM v1,VectorM v2); // CROSS product +VectorM planelineintersection(VectorM n,float d,VectorM p1,VectorM p2); + +class matrix{ + public: + VectorM x,y,z; + matrix(){x=VectorM(1.0f,0.0f,0.0f); + y=VectorM(0.0f,1.0f,0.0f); + z=VectorM(0.0f,0.0f,1.0f);}; + matrix(VectorM _x,VectorM _y,VectorM _z){x=_x;y=_y;z=_z;}; +}; +matrix transpose(matrix m); +VectorM operator*(matrix m,VectorM v); +matrix operator*(matrix m1,matrix m2); + +class Quaternion{ + public: + float r,x,y,z; + Quaternion(){x=y=z=0.0f;r=1.0f;}; + Quaternion(VectorM v,float t){v=normalize(v);r=(float)cos(t/2.0);v=v*(float)sin(t/2.0);x=v.x;y=v.y;z=v.z;}; + Quaternion(float _r,float _x,float _y,float _z){r=_r;x=_x;y=_y;z=_z;}; + float angle(){return (float)(acos(r)*2.0);} + VectorM axis(){VectorM a(x,y,z); return a*(float)(1/sin(angle()/2.0));} + VectorM xdir(){return VectorM(1-2*(y*y+z*z), 2*(x*y+r*z), 2*(x*z-r*y));} + VectorM ydir(){return VectorM( 2*(x*y-r*z),1-2*(x*x+z*z), 2*(y*z+r*x));} + VectorM zdir(){return VectorM( 2*(x*z+r*y), 2*(y*z-r*x),1-2*(x*x+y*y));} + matrix getmatrix(){return matrix(xdir(),ydir(),zdir());} + //operator matrix(){return getmatrix();} +}; +Quaternion operator-(Quaternion q); +Quaternion operator*(Quaternion a,Quaternion b); +VectorM operator*(Quaternion q,VectorM v); +VectorM operator*(VectorM v,Quaternion q); +Quaternion slerp(Quaternion a,Quaternion b,float interp); + +#endif diff --git a/library/PolyVoxCore/source/IndexedSurfacePatch.cpp b/library/PolyVoxCore/source/IndexedSurfacePatch.cpp index c64354e0..91fa4930 100644 --- a/library/PolyVoxCore/source/IndexedSurfacePatch.cpp +++ b/library/PolyVoxCore/source/IndexedSurfacePatch.cpp @@ -21,6 +21,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "IndexedSurfacePatch.h" +#include "progmesh.h" + using namespace std; namespace PolyVox @@ -331,4 +333,273 @@ namespace PolyVox return result; } + + /*int IndexedSurfacePatch::countMaterialBoundary(void) + { + int count = 0; + for(int ct = 0; ct < m_vecVertices.size(); ct++) + { + if(m_vecVertices[ct].m_bIsMaterialEdgeVertex) + { + count++; + } + } + return count; + } + + void IndexedSurfacePatch::growMaterialBoundary(void) + { + std::vector vecNewVertices = m_vecVertices; + + for(vector::iterator iterIndex = m_vecTriangleIndices.begin(); iterIndex != m_vecTriangleIndices.end();) + { + SurfaceVertex& v0 = m_vecVertices[*iterIndex]; + SurfaceVertex& v0New = vecNewVertices[*iterIndex]; + iterIndex++; + SurfaceVertex& v1 = m_vecVertices[*iterIndex]; + SurfaceVertex& v1New = vecNewVertices[*iterIndex]; + iterIndex++; + SurfaceVertex& v2 = m_vecVertices[*iterIndex]; + SurfaceVertex& v2New = vecNewVertices[*iterIndex]; + iterIndex++; + + if(v0.m_bIsMaterialEdgeVertex || v1.m_bIsMaterialEdgeVertex || v2.m_bIsMaterialEdgeVertex) + { + v0New.m_bIsMaterialEdgeVertex = true; + v1New.m_bIsMaterialEdgeVertex = true; + v2New.m_bIsMaterialEdgeVertex = true; + } + } + + m_vecVertices = vecNewVertices; + }*/ + + void IndexedSurfacePatch::makeProgressiveMesh(void) + { + + //Build the mesh using Stan Melax's code + List vecList; + for(int vertCt = 0; vertCt < m_vecVertices.size(); vertCt++) + { + VectorM vec; + vec.x = m_vecVertices[vertCt].getPosition().getX(); + vec.y = m_vecVertices[vertCt].getPosition().getY(); + vec.z = m_vecVertices[vertCt].getPosition().getZ(); + + if(m_vecVertices[vertCt].isEdgeVertex() || m_vecVertices[vertCt].m_bIsMaterialEdgeVertex) + { + vec.fBoundaryCost = 1.0f; + } + else + { + vec.fBoundaryCost = 0.0f; + } + + vecList.Add(vec); + } + + List triList; + for(int triCt = 0; triCt < m_vecTriangleIndices.size(); ) + { + tridata tri; + tri.v[0] = m_vecTriangleIndices[triCt]; + triCt++; + tri.v[1] = m_vecTriangleIndices[triCt]; + triCt++; + tri.v[2] = m_vecTriangleIndices[triCt]; + triCt++; + triList.Add(tri); + } + + List map; + List permutation; + + ProgressiveMesh(vecList, triList, map, permutation); + + //Apply the permutation to our vertices + std::vector vecNewVertices(m_vecVertices.size()); + for(int vertCt = 0; vertCt < m_vecVertices.size(); vertCt++) + { + vecNewVertices[permutation[vertCt]]= m_vecVertices[vertCt]; + } + + std::vector vecNewTriangleIndices(m_vecTriangleIndices.size()); + for(int triCt = 0; triCt < m_vecTriangleIndices.size(); triCt++) + { + vecNewTriangleIndices[triCt] = permutation[m_vecTriangleIndices[triCt]]; + } + + m_vecVertices = vecNewVertices; + m_vecTriangleIndices = vecNewTriangleIndices; + + //////////////////////////////////////////////////////////////////////////////// + //Check for unused vertices? + //int usedVertices = 0; + //int unusedVertices = 0; + /*usedVertices = 0; + unusedVertices = 0; + for(int vertCt = 0; vertCt < isp->m_vecVertices.size(); vertCt++) + { + bool found = false; + for(int triCt = 0; triCt < isp->m_vecTriangleIndices.size(); triCt++) + { + if(vertCt == isp->m_vecTriangleIndices[triCt]) + { + found = true; + break; + } + } + if(found) + { + usedVertices++; + } + else + { + unusedVertices++; + } + } + + std::cout << "Used = " << usedVertices << std::endl; + std::cout << "Unused = " << unusedVertices << std::endl;*/ + //////////////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////////////// + + //switch triangle order? + /*int noOfTriIndices = isp->m_vecTriangleIndices.size(); + for(int triCt = 0; triCt < noOfTriIndices; triCt++) + { + vecNewTriangleIndices[(noOfTriIndices - 1) - triCt] = isp->m_vecTriangleIndices[triCt]; + } + isp->m_vecTriangleIndices = vecNewTriangleIndices;*/ + + //Now build the new index buffers + std::vector vecNewTriangles; + std::vector vecUnaffectedTriangles; + std::vector vecCollapsedTriangles; + + vector vecCanCollapse(m_vecVertices.size()); + for(int ct = 0; ct < vecCanCollapse.size(); ct++) + { + vecCanCollapse[ct] = true; + } + + vector vecTriangleRemoved(m_vecTriangleIndices.size() / 3); + for(int ct = 0; ct < vecTriangleRemoved.size(); ct++) + { + vecTriangleRemoved[ct] = false; + } + + int noOfCollapsed = 0; + m_vecLodRecords.clear(); + + + for(int vertToCollapse = m_vecVertices.size() - 1; vertToCollapse > 0; vertToCollapse--) + //int vertToCollapse = isp->m_vecVertices.size() - 1; + { + int vertCollapseTarget = map[vertToCollapse]; + + if((vecCanCollapse[vertToCollapse]) && (vecCanCollapse[vertCollapseTarget])) + { + int noOfNew = 0; + + for(int triCt = 0; triCt < m_vecTriangleIndices.size();) + { + int v0 = m_vecTriangleIndices[triCt]; + triCt++; + int v1 = m_vecTriangleIndices[triCt]; + triCt++; + int v2 = m_vecTriangleIndices[triCt]; + triCt++; + + if(vecTriangleRemoved[(triCt - 3) / 3] == false) + { + if( (v0 == vertToCollapse) || (v1 == vertToCollapse) || (v2 == vertToCollapse) ) + { + vecCollapsedTriangles.push_back(v0); + vecCollapsedTriangles.push_back(v1); + vecCollapsedTriangles.push_back(v2); + + vecCanCollapse[v0] = false; + vecCanCollapse[v1] = false; + vecCanCollapse[v2] = false; + + noOfCollapsed++; + + int targetV0 = v0; + int targetV1 = v1; + int targetV2 = v2; + + if(targetV0 == vertToCollapse) targetV0 = vertCollapseTarget; + if(targetV1 == vertToCollapse) targetV1 = vertCollapseTarget; + if(targetV2 == vertToCollapse) targetV2 = vertCollapseTarget; + + if((targetV0 != targetV1) && (targetV1 != targetV2) && (targetV2 != targetV0)) + { + vecNewTriangles.push_back(targetV0); + vecNewTriangles.push_back(targetV1); + vecNewTriangles.push_back(targetV2); + + noOfNew++; + + vecCanCollapse[targetV0] = false; + vecCanCollapse[targetV1] = false; + vecCanCollapse[targetV2] = false; + } + + vecTriangleRemoved[(triCt - 3) / 3] = true; + + + } + } + } + LodRecord lodRecord; + lodRecord.beginIndex = vecNewTriangles.size() - (3 * noOfNew); + lodRecord.endIndex = vecCollapsedTriangles.size(); + m_vecLodRecords.push_back(lodRecord); + } + } + + //Copy triangles into unaffected list + for(int triCt = 0; triCt < m_vecTriangleIndices.size();) + { + int v0 = m_vecTriangleIndices[triCt]; + triCt++; + int v1 = m_vecTriangleIndices[triCt]; + triCt++; + int v2 = m_vecTriangleIndices[triCt]; + triCt++; + + if(vecTriangleRemoved[(triCt - 3) / 3] == false) + { + vecUnaffectedTriangles.push_back(v0); + vecUnaffectedTriangles.push_back(v1); + vecUnaffectedTriangles.push_back(v2); + } + } + + //Now copy the three lists of triangles back + m_vecTriangleIndices.clear(); + + for(int ct = 0; ct < vecNewTriangles.size(); ct++) + { + m_vecTriangleIndices.push_back(vecNewTriangles[ct]); + } + + for(int ct = 0; ct < vecUnaffectedTriangles.size(); ct++) + { + m_vecTriangleIndices.push_back(vecUnaffectedTriangles[ct]); + } + + for(int ct = 0; ct < vecCollapsedTriangles.size(); ct++) + { + m_vecTriangleIndices.push_back(vecCollapsedTriangles[ct]); + } + + //Adjust the lod records + for(int ct = 0; ct < m_vecLodRecords.size(); ct++) + { + m_vecLodRecords[ct].endIndex += (vecNewTriangles.size() + vecUnaffectedTriangles.size()); + } + } } diff --git a/library/PolyVoxCore/source/SurfaceExtractor.cpp b/library/PolyVoxCore/source/SurfaceExtractor.cpp index 042cd8f0..8ce54c69 100644 --- a/library/PolyVoxCore/source/SurfaceExtractor.cpp +++ b/library/PolyVoxCore/source/SurfaceExtractor.cpp @@ -90,6 +90,12 @@ namespace PolyVox m_ispCurrent->m_Region = m_regInputUncropped; + m_ispCurrent->m_vecLodRecords.clear(); + LodRecord lodRecord; + lodRecord.beginIndex = 0; + lodRecord.endIndex = m_ispCurrent->getNoOfIndices(); + m_ispCurrent->m_vecLodRecords.push_back(lodRecord); + return POLYVOX_SHARED_PTR(m_ispCurrent); } diff --git a/library/PolyVoxCore/source/progmesh.cpp b/library/PolyVoxCore/source/progmesh.cpp new file mode 100644 index 00000000..30b93779 --- /dev/null +++ b/library/PolyVoxCore/source/progmesh.cpp @@ -0,0 +1,314 @@ +/* + * Progressive Mesh type Polygon Reduction Algorithm + * by Stan Melax (c) 1998 + * Permission to use any of this code wherever you want is granted.. + * Although, please do acknowledge authorship if appropriate. + * + * See the header file progmesh.h for a description of this module + */ + +#include +#include +#include +#include +#include + +#include + +#include "vector_melax.h" +#include "list.h" +#include "progmesh.h" + +/* + * For the polygon reduction algorithm we use data structures + * that contain a little bit more information than the usual + * indexed face set type of data structure. + * From a vertex we wish to be able to quickly get the + * neighboring faces and vertices. + */ +class Triangle; +class Vertex; + +class Triangle { + public: + Vertex * vertex[3]; // the 3 points that make this tri + VectorM normal; // unit vector othogonal to this face + Triangle(Vertex *v0,Vertex *v1,Vertex *v2); + ~Triangle(); + void ComputeNormal(); + void ReplaceVertex(Vertex *vold,Vertex *vnew); + int HasVertex(Vertex *v); +}; + +class Vertex { + public: + VectorM position; // location of point in euclidean space + int id; // place of vertex in original list + List neighbor; // adjacent vertices + List face; // adjacent triangles + float objdist; // cached cost of collapsing edge + Vertex * collapse; // candidate vertex for collapse + Vertex(VectorM v,int _id); + ~Vertex(); + void RemoveIfNonNeighbor(Vertex *n); +}; +List vertices; +List triangles; + + +Triangle::Triangle(Vertex *v0,Vertex *v1,Vertex *v2){ + assert(v0!=v1 && v1!=v2 && v2!=v0); + vertex[0]=v0; + vertex[1]=v1; + vertex[2]=v2; + ComputeNormal(); + triangles.Add(this); + for(int i=0;i<3;i++) { + vertex[i]->face.Add(this); + for(int j=0;j<3;j++) if(i!=j) { + vertex[i]->neighbor.AddUnique(vertex[j]); + } + } +} +Triangle::~Triangle(){ + int i; + triangles.Remove(this); + for(i=0;i<3;i++) { + if(vertex[i]) vertex[i]->face.Remove(this); + } + for(i=0;i<3;i++) { + int i2 = (i+1)%3; + if(!vertex[i] || !vertex[i2]) continue; + vertex[i ]->RemoveIfNonNeighbor(vertex[i2]); + vertex[i2]->RemoveIfNonNeighbor(vertex[i ]); + } +} +int Triangle::HasVertex(Vertex *v) { + return (v==vertex[0] ||v==vertex[1] || v==vertex[2]); +} +void Triangle::ComputeNormal(){ + VectorM v0=vertex[0]->position; + VectorM v1=vertex[1]->position; + VectorM v2=vertex[2]->position; + normal = (v1-v0)*(v2-v1); + if(magnitude(normal)==0)return; + normal = normalize(normal); +} +void Triangle::ReplaceVertex(Vertex *vold,Vertex *vnew) { + assert(vold && vnew); + assert(vold==vertex[0] || vold==vertex[1] || vold==vertex[2]); + assert(vnew!=vertex[0] && vnew!=vertex[1] && vnew!=vertex[2]); + if(vold==vertex[0]){ + vertex[0]=vnew; + } + else if(vold==vertex[1]){ + vertex[1]=vnew; + } + else { + assert(vold==vertex[2]); + vertex[2]=vnew; + } + int i; + vold->face.Remove(this); + assert(!vnew->face.Contains(this)); + vnew->face.Add(this); + for(i=0;i<3;i++) { + vold->RemoveIfNonNeighbor(vertex[i]); + vertex[i]->RemoveIfNonNeighbor(vold); + } + for(i=0;i<3;i++) { + assert(vertex[i]->face.Contains(this)==1); + for(int j=0;j<3;j++) if(i!=j) { + vertex[i]->neighbor.AddUnique(vertex[j]); + } + } + ComputeNormal(); +} + +Vertex::Vertex(VectorM v,int _id) { + position =v; + id=_id; + vertices.Add(this); +} + +Vertex::~Vertex(){ + assert(face.num==0); + while(neighbor.num) { + neighbor[0]->neighbor.Remove(this); + neighbor.Remove(neighbor[0]); + } + vertices.Remove(this); +} +void Vertex::RemoveIfNonNeighbor(Vertex *n) { + // removes n from neighbor list if n isn't a neighbor. + if(!neighbor.Contains(n)) return; + for(int i=0;iHasVertex(n)) return; + } + neighbor.Remove(n); +} + + +float ComputeEdgeCollapseCost(Vertex *u,Vertex *v) { + // if we collapse edge uv by moving u to v then how + // much different will the model change, i.e. how much "error". + // Texture, vertex normal, and border vertex code was removed + // to keep this demo as simple as possible. + // The method of determining cost was designed in order + // to exploit small and coplanar regions for + // effective polygon reduction. + // Is is possible to add some checks here to see if "folds" + // would be generated. i.e. normal of a remaining face gets + // flipped. I never seemed to run into this problem and + // therefore never added code to detect this case. + int i; + float edgelength = magnitude(v->position - u->position); + float curvature=0; + + // find the "sides" triangles that are on the edge uv + List sides; + for(i=0;iface.num;i++) { + if(u->face[i]->HasVertex(v)){ + sides.Add(u->face[i]); + } + } + // use the triangle facing most away from the sides + // to determine our curvature term + for(i=0;iface.num;i++) { + float mincurv=1; // curve for face i and closer side to it + for(int j=0;jface[i]->normal ^ sides[j]->normal; + mincurv = min(mincurv,(1-dotprod)/2.0f); + } + curvature = max(curvature,mincurv); + } + float boundaryCost = u->position.fBoundaryCost + v->position.fBoundaryCost; + // the more coplanar the lower the curvature term + return edgelength * curvature + boundaryCost; +} + +void ComputeEdgeCostAtVertex(Vertex *v) { + // compute the edge collapse cost for all edges that start + // from vertex v. Since we are only interested in reducing + // the object by selecting the min cost edge at each step, we + // only cache the cost of the least cost edge at this vertex + // (in member variable collapse) as well as the value of the + // cost (in member variable objdist). + if(v->neighbor.num==0) { + // v doesn't have neighbors so it costs nothing to collapse + v->collapse=NULL; + v->objdist=-0.01f; + return; + } + v->objdist = 1000000; + v->collapse=NULL; + // search all neighboring edges for "least cost" edge + for(int i=0;ineighbor.num;i++) { + float dist; + dist = ComputeEdgeCollapseCost(v,v->neighbor[i]); + //std::cout << "Cost: " << dist << std::endl; + if(distobjdist) { + v->collapse=v->neighbor[i]; // candidate for edge collapse + v->objdist=dist; // cost of the collapse + } + } +} +void ComputeAllEdgeCollapseCosts() { + // For all the edges, compute the difference it would make + // to the model if it was collapsed. The least of these + // per vertex is cached in each vertex object. + for(int i=0;itmp; + // make tmp a list of all the neighbors of u + for(i=0;ineighbor.num;i++) { + tmp.Add(u->neighbor[i]); + } + // delete triangles on edge uv: + for(i=u->face.num-1;i>=0;i--) { + if(u->face[i]->HasVertex(v)) { + delete(u->face[i]); + } + } + // update remaining triangles to have v instead of u + for(i=u->face.num-1;i>=0;i--) { + u->face[i]->ReplaceVertex(u,v); + } + delete u; + // recompute the edge collapse costs for neighboring vertices + for(i=0;i &vert){ + for(int i=0;i &tri){ + for(int i=0;iobjdist < mn->objdist) { + mn = vertices[i]; + } + } + return mn; +} + +void ProgressiveMesh(List &vert, List &tri, + List &map, List &permutation) +{ + AddVertex(vert); // put input data into our data structures + AddFaces(tri); + ComputeAllEdgeCollapseCosts(); // cache all edge collapse costs + permutation.SetSize(vertices.num); // allocate space + map.SetSize(vertices.num); // allocate space + // reduce the object down to nothing: + while(vertices.num > 0) { + // get the next vertex to collapse + Vertex *mn = MinimumCostEdge(); + // keep track of this vertex, i.e. the collapse ordering + permutation[mn->id]=vertices.num-1; + // keep track of vertex to which we collapse to + map[vertices.num-1] = (mn->collapse)?mn->collapse->id:-1; + // Collapse this edge + Collapse(mn,mn->collapse); + } + // reorder the map list based on the collapse ordering + for(int i=0;i +#include +#include + +#include "vector_melax.h" + +float sqr(float a) {return a*a;} + +// vector (floating point) implementation + +float magnitude(VectorM v) { + return (float)sqrt(sqr(v.x) + sqr( v.y)+ sqr(v.z)); +} +VectorM normalize(VectorM v) { + float d=magnitude(v); + if (d==0) { + printf("Cant normalize ZERO vector\n"); + assert(0); + d=0.1f; + } + v.x/=d; + v.y/=d; + v.z/=d; + return v; +} + +VectorM operator+(VectorM v1,VectorM v2) {return VectorM(v1.x+v2.x,v1.y+v2.y,v1.z+v2.z);} +VectorM operator-(VectorM v1,VectorM v2) {return VectorM(v1.x-v2.x,v1.y-v2.y,v1.z-v2.z);} +VectorM operator-(VectorM v) {return VectorM(-v.x,-v.y,-v.z);} +VectorM operator*(VectorM v1,float s) {return VectorM(v1.x*s,v1.y*s,v1.z*s);} +VectorM operator*(float s, VectorM v1) {return VectorM(v1.x*s,v1.y*s,v1.z*s);} +VectorM operator/(VectorM v1,float s) {return v1*(1.0f/s);} +float operator^(VectorM v1,VectorM v2) {return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z;} +VectorM operator*(VectorM v1,VectorM v2) { + return VectorM( + v1.y * v2.z - v1.z*v2.y, + v1.z * v2.x - v1.x*v2.z, + v1.x * v2.y - v1.y*v2.x); +} +VectorM planelineintersection(VectorM n,float d,VectorM p1,VectorM p2){ + // returns the point where the line p1-p2 intersects the plane n&d + VectorM dif = p2-p1; + float dn= n^dif; + float t = -(d+(n^p1) )/dn; + return p1 + (dif*t); +} +int concurrent(VectorM a,VectorM b) { + return(a.x==b.x && a.y==b.y && a.z==b.z); +} + + +// Matrix Implementation +matrix transpose(matrix m) { + return matrix( VectorM(m.x.x,m.y.x,m.z.x), + VectorM(m.x.y,m.y.y,m.z.y), + VectorM(m.x.z,m.y.z,m.z.z)); +} +VectorM operator*(matrix m,VectorM v){ + m=transpose(m); // since column ordered + return VectorM(m.x^v,m.y^v,m.z^v); +} +matrix operator*(matrix m1,matrix m2){ + m1=transpose(m1); + return matrix(m1*m2.x,m1*m2.y,m1*m2.z); +} + +//Quaternion Implementation +Quaternion operator*(Quaternion a,Quaternion b) { + Quaternion c; + c.r = a.r*b.r - a.x*b.x - a.y*b.y - a.z*b.z; + c.x = a.r*b.x + a.x*b.r + a.y*b.z - a.z*b.y; + c.y = a.r*b.y - a.x*b.z + a.y*b.r + a.z*b.x; + c.z = a.r*b.z + a.x*b.y - a.y*b.x + a.z*b.r; + return c; +} +Quaternion operator-(Quaternion q) { + return Quaternion(q.r*-1,q.x,q.y,q.z); +} +Quaternion operator*(Quaternion a,float b) { + return Quaternion(a.r*b, a.x*b, a.y*b, a.z*b); +} +VectorM operator*(Quaternion q,VectorM v) { + return q.getmatrix() * v; +} +VectorM operator*(VectorM v,Quaternion q){ + assert(0); // must multiply with the quat on the left + return VectorM(0.0f,0.0f,0.0f); +} + +Quaternion operator+(Quaternion a,Quaternion b) { + return Quaternion(a.r+b.r, a.x+b.x, a.y+b.y, a.z+b.z); +} +float operator^(Quaternion a,Quaternion b) { + return (a.r*b.r + a.x*b.x + a.y*b.y + a.z*b.z); +} +Quaternion slerp(Quaternion a,Quaternion b,float interp){ + if((a^b) <0.0) { + a.r=-a.r; + a.x=-a.x; + a.y=-a.y; + a.z=-a.z; + } + float theta = (float)acos(a^b); + if(theta==0.0f) { return(a);} + return a*(float)(sin(theta-interp*theta)/sin(theta)) + b*(float)(sin(interp*theta)/sin(theta)); +} + diff --git a/library/PolyVoxUtil/CMakeLists.txt b/library/PolyVoxUtil/CMakeLists.txt index b3643661..af6d0a02 100644 --- a/library/PolyVoxUtil/CMakeLists.txt +++ b/library/PolyVoxUtil/CMakeLists.txt @@ -50,6 +50,11 @@ IF(WIN32) #Install the util header files. INSTALL(DIRECTORY include DESTINATION PolyVoxUtil COMPONENT development PATTERN "*.svn*" EXCLUDE) + + #On windows, we also install the debug information. It's unfortunate that we have to hard-code + #the 'Debug' part of the path, but CMake doesn't seem to provide a way around this. The best I + #found was: http://www.cmake.org/pipermail/cmake/2007-October/016924.html (and it is a bit ugly). + INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/Debug/PolyVoxUtil_d.pdb DESTINATION PolyVoxUtil/lib CONFIGURATIONS Debug) ELSE(WIN32) INSTALL(TARGETS PolyVoxUtil RUNTIME DESTINATION bin