// // Copyright (C) Pixar. All rights reserved. // // This license governs use of the accompanying software. If you // use the software, you accept this license. If you do not accept // the license, do not use the software. // // 1. Definitions // The terms "reproduce," "reproduction," "derivative works," and // "distribution" have the same meaning here as under U.S. // copyright law. A "contribution" is the original software, or // any additions or changes to the software. // A "contributor" is any person or entity that distributes its // contribution under this license. // "Licensed patents" are a contributor's patent claims that read // directly on its contribution. // // 2. Grant of Rights // (A) Copyright Grant- Subject to the terms of this license, // including the license conditions and limitations in section 3, // each contributor grants you a non-exclusive, worldwide, // royalty-free copyright license to reproduce its contribution, // prepare derivative works of its contribution, and distribute // its contribution or any derivative works that you create. // (B) Patent Grant- Subject to the terms of this license, // including the license conditions and limitations in section 3, // each contributor grants you a non-exclusive, worldwide, // royalty-free license under its licensed patents to make, have // made, use, sell, offer for sale, import, and/or otherwise // dispose of its contribution in the software or derivative works // of the contribution in the software. // // 3. Conditions and Limitations // (A) No Trademark License- This license does not grant you // rights to use any contributor's name, logo, or trademarks. // (B) If you bring a patent claim against any contributor over // patents that you claim are infringed by the software, your // patent license from such contributor to the software ends // automatically. // (C) If you distribute any portion of the software, you must // retain all copyright, patent, trademark, and attribution // notices that are present in the software. // (D) If you distribute any portion of the software in source // code form, you may do so only under this license by including a // complete copy of this license with your distribution. If you // distribute any portion of the software in compiled or object // code form, you may only do so under a license that complies // with this license. // (E) The software is licensed "as-is." You bear the risk of // using it. The contributors give no express warranties, // guarantees or conditions. You may have additional consumer // rights under your local laws which this license cannot change. // To the extent permitted under your local laws, the contributors // exclude the implied warranties of merchantability, fitness for // a particular purpose and non-infringement. // #ifndef HBRMESH_H #define HBRMESH_H #ifdef PRMAN #include "libtarget/TgMalloc.h" // only for alloca #include "libtarget/TgThread.h" #ifdef HBRSTITCH #include "libtarget/TgHashMap.h" #endif #endif #include #include #include #include #include #include #include "../hbr/vertex.h" #include "../hbr/face.h" #include "../hbr/hierarchicalEdit.h" #include "../hbr/vertexEdit.h" #include "../hbr/creaseEdit.h" #include "../hbr/allocator.h" #include "../version.h" namespace OpenSubdiv { namespace OPENSUBDIV_VERSION { template class HbrSubdivision; template class HbrHalfedge; template class HbrMesh { public: HbrMesh(HbrSubdivision* subdivision = 0, int fvarcount = 0, const int *fvarindices = 0, const int *fvarwidths = 0, int totalfvarwidth = 0 #ifdef HBRSTITCH , int stitchCount = 0 #endif ); ~HbrMesh(); // Create vertex with the indicated ID and data HbrVertex* NewVertex(int id, const T &data); // Create vertex with the indicated data. The ID will be assigned // by the mesh. HbrVertex* NewVertex(const T &data); // Create vertex without an ID - one will be assigned by the mesh, // and the data implicitly created will share the same id HbrVertex* NewVertex(); // Ask for vertex with the indicated ID HbrVertex* GetVertex(int id) const { if (id >= nvertices) { return 0; } else { return vertices[id]; } } // Ask for client data associated with the vertex with the indicated ID void* GetVertexClientData(int id) const { if (id >= vertexClientData.size()) { return 0; } else { return vertexClientData[id]; } } // Set client data associated with the vertex with the indicated ID void SetVertexClientData(int id, void *data) { if (id >= vertexClientData.size()) { size_t oldsize = vertexClientData.size(); vertexClientData.resize(nvertices); if (s_memStatsIncrement) { s_memStatsIncrement((vertexClientData.size() - oldsize) * sizeof(void*)); } } vertexClientData[id] = data; } // Create face from a list of vertex IDs HbrFace* NewFace(int nvertices, const int *vtx, int uindex); // Create face from a list of vertices HbrFace* NewFace(int nvertices, HbrVertex** vtx, HbrFace* parent, int childindex); // "Create" a new uniform index int NewUniformIndex() { return ++maxUniformIndex; } // Finishes initialization of the mesh void Finish(); // Remove the indicated face from the mesh void DeleteFace(HbrFace* face); // Remove the indicated vertex from the mesh void DeleteVertex(HbrVertex* vertex); // Returns number of vertices in the mesh int GetNumVertices() const; // Returns number of disconnected vertices in the mesh int GetNumDisconnectedVertices() const; // Returns number of faces in the mesh int GetNumFaces() const; // Returns number of coarse faces in the mesh int GetNumCoarseFaces() const; // Ask for face with the indicated ID HbrFace* GetFace(int id) const; // Ask for client data associated with the face with the indicated ID void* GetFaceClientData(int id) const { if (id >= faceClientData.size()) { return 0; } else { return faceClientData[id]; } } // Set client data associated with the face with the indicated ID void SetFaceClientData(int id, void *data) { if (id >= faceClientData.size()) { size_t oldsize = faceClientData.size(); faceClientData.resize(nfaces); if (s_memStatsIncrement) { s_memStatsIncrement((faceClientData.size() - oldsize) * sizeof(void*)); } } faceClientData[id] = data; } // Returns a collection of all vertices in the mesh. This function // requires an output iterator; to get the vertices into a // std::vector, use GetVertices(std::back_inserter(myvector)) template void GetVertices(OutputIterator vertices) const; // Applies operator to all vertices void ApplyOperatorAllVertices(HbrVertexOperator &op) const; // Returns a collection of all faces in the mesh. This function // requires an output iterator; to get the faces into a // std::vector, use GetFaces(std::back_inserter(myvector)) template void GetFaces(OutputIterator faces) const; // Returns the subdivision method HbrSubdivision* GetSubdivision() const { return subdivision; } // Return the number of facevarying variables int GetFVarCount() const { return fvarcount; } // Return a table of the start index of each facevarying variable const int *GetFVarIndices() const { return fvarindices; } // Return a table of the size of each facevarying variable const int *GetFVarWidths() const { return fvarwidths; } // Return the sum size of facevarying variables per vertex int GetTotalFVarWidth() const { return totalfvarwidth; } #ifdef HBRSTITCH int GetStitchCount() const { return stitchCount; } #endif void PrintStats(std::ostream& out); // Returns memory statistics size_t GetMemStats() const { return m_memory; } // Interpolate boundary management enum InterpolateBoundaryMethod { k_InterpolateBoundaryNone, k_InterpolateBoundaryEdgeOnly, k_InterpolateBoundaryEdgeAndCorner, k_InterpolateBoundaryAlwaysSharp }; InterpolateBoundaryMethod GetInterpolateBoundaryMethod() const { return interpboundarymethod; } void SetInterpolateBoundaryMethod(InterpolateBoundaryMethod method) { interpboundarymethod = method; } InterpolateBoundaryMethod GetFVarInterpolateBoundaryMethod() const { return fvarinterpboundarymethod; } void SetFVarInterpolateBoundaryMethod(InterpolateBoundaryMethod method) { fvarinterpboundarymethod = method; } bool GetFVarPropagateCorners() const { return fvarpropagatecorners; } void SetFVarPropagateCorners(bool p) { fvarpropagatecorners = p; } // Register routines for keeping track of memory usage void RegisterMemoryRoutines(void (*increment)(unsigned long bytes), void (*decrement)(unsigned long bytes)) { m_faceAllocator.SetMemStatsIncrement(increment); m_faceAllocator.SetMemStatsDecrement(decrement); m_vertexAllocator.SetMemStatsIncrement(increment); m_vertexAllocator.SetMemStatsDecrement(decrement); s_memStatsIncrement = increment; s_memStatsDecrement = decrement; } // Add a vertex to consider for garbage collection. All // neighboring faces of that vertex will be examined to see if // they can be deleted void AddGarbageCollectableVertex(HbrVertex* vertex) { if (!m_transientMode) { assert(vertex); if (!vertex->IsCollected()) { gcVertices.push_back(vertex); vertex->SetCollected(); } } } // Apply garbage collection to the mesh void GarbageCollect(); // Add a new hierarchical edit to the mesh void AddHierarchicalEdit(HbrHierarchicalEdit* edit); // Return the hierarchical edits associated with the mesh const std::vector*> &GetHierarchicalEdits() const { return hierarchicalEdits; } // Return the hierarchical edits associated with the mesh at an // offset HbrHierarchicalEdit** GetHierarchicalEditsAtOffset(int offset) { return &hierarchicalEdits[offset]; } // Whether the mesh has certain types of edits bool HasVertexEdits() const { return hasVertexEdits; } bool HasCreaseEdits() const { return hasCreaseEdits; } void Unrefine(int numCoarseVerts, int numCoarseFaces) { static int oldMaxFaceID = 0; if(oldMaxFaceID == 0) { oldMaxFaceID = numCoarseFaces; } for (int i = numCoarseFaces; i < maxFaceID; ++i) { HbrFace* f = GetFace(i); if(f and not f->IsCoarse()) DeleteFace(f); } //oldMaxFaceID = maxFaceID; maxFaceID = numCoarseFaces; for(int i=numCoarseVerts; (int)vertices.size(); ++i ) { HbrVertex* v = GetVertex(i); if(v and not v->IsReferenced()) DeleteVertex(v); } } // When mode is true, the mesh is put in a "transient" mode, // i.e. all subsequent intermediate vertices/faces that are // created by subdivision are deemed temporary. This transient // data can be entirely freed by a subsequent call to // FreeTransientData(). Essentially, the mesh is checkpointed and // restored. This is useful when space is at a premium and // subdivided results are cached elsewhere. On the other hand, // repeatedly putting the mesh in and out of transient mode and // performing the same evaluations comes at a significant compute // cost. void SetTransientMode(bool mode) { m_transientMode = mode; } // Frees transient subdivision data; returns the mesh to a // checkpointed state prior to a call to SetTransientMode. void FreeTransientData(); // Create new face children block for use by HbrFace HbrFaceChildren* NewFaceChildren() { return m_faceChildrenAllocator.Allocate(); } // Recycle face children block used by HbrFace void DeleteFaceChildren(HbrFaceChildren* facechildren) { m_faceChildrenAllocator.Deallocate(facechildren); } #ifdef HBRSTITCH void * GetStitchData(const HbrHalfedge* edge) const { typename TgHashMap*, void *>::const_iterator i = stitchData.find(edge); if (i != stitchData.end()) { return i->second; } else { return NULL; } } void SetStitchData(const HbrHalfedge* edge, void *data) { stitchData[edge] = data; } #endif private: // Subdivision method used in this mesh HbrSubdivision* subdivision; // Number of facevarying datums int fvarcount; // Start indices of the facevarying data we want to store const int *fvarindices; // Individual widths of the facevarying data we want to store const int *fvarwidths; // Total widths of the facevarying data const int totalfvarwidth; #ifdef HBRSTITCH // Number of stitch edges per halfedge const int stitchCount; // Client (sparse) data used on some halfedges TgHashMap*, void *> stitchData; #endif // Vertices which comprise this mesh std::vector *> vertices; int nvertices; // Client data associated with each face std::vector vertexClientData; // Faces which comprise this mesh std::vector *> faces; int nfaces; // Client data associated with each face std::vector faceClientData; // Maximum vertex ID - may be needed when generating a unique // vertex ID int maxVertexID; // Maximum face ID - needed when generating a unique face ID int maxFaceID; // Maximum uniform index - needed to generate a new uniform index int maxUniformIndex; // Boundary interpolation method InterpolateBoundaryMethod interpboundarymethod; // Facevarying boundary interpolation method InterpolateBoundaryMethod fvarinterpboundarymethod; // Whether facevarying corners propagate their sharpness bool fvarpropagatecorners; // Memory statistics tracking routines HbrMemStatFunction s_memStatsIncrement; HbrMemStatFunction s_memStatsDecrement; // Vertices which may be garbage collected std::vector*> gcVertices; // List of vertex IDs which may be recycled std::set recycleIDs; // Hierarchical edits. This vector is left unsorted until Finish() // is called, at which point it is sorted. After that point, // HbrFaces have pointers directly into this array so manipulation // of it should be avoided. std::vector*> hierarchicalEdits; // Size of faces (including 4 facevarying bits and stitch edges) const size_t m_faceSize; HbrAllocator > m_faceAllocator; // Size of vertices (includes storage for one piece of facevarying data) const size_t m_vertexSize; HbrAllocator > m_vertexAllocator; // Allocator for face children blocks used by HbrFace HbrAllocator > m_faceChildrenAllocator; // Memory used by this mesh alone, plus all its faces and vertices size_t m_memory; // Number of coarse faces. Initialized at Finish() int m_numCoarseFaces; // Flags which indicate whether the mesh has certain types of // edits unsigned hasVertexEdits:1; unsigned hasCreaseEdits:1; // True if the mesh is in "transient" mode, meaning all // vertices/faces that are created via NewVertex/NewFace should be // deemed temporary bool m_transientMode; // Vertices which are transient std::vector*> m_transientVertices; // Faces which are transient std::vector*> m_transientFaces; }; } // end namespace OPENSUBDIV_VERSION using namespace OPENSUBDIV_VERSION; } // end namespace OpenSubdiv #include #include "../hbr/mesh.h" #include "../hbr/halfedge.h" namespace OpenSubdiv { namespace OPENSUBDIV_VERSION { template HbrMesh::HbrMesh(HbrSubdivision* s, int _fvarcount, const int *_fvarindices, const int *_fvarwidths, int _totalfvarwidth #ifdef HBRSTITCH , int _stitchCount #endif ) : subdivision(s), fvarcount(_fvarcount), fvarindices(_fvarindices), fvarwidths(_fvarwidths), totalfvarwidth(_totalfvarwidth), #ifdef HBRSTITCH stitchCount(_stitchCount), #endif nvertices(0), nfaces(0), maxVertexID(0), maxFaceID(0), maxUniformIndex(0), interpboundarymethod(k_InterpolateBoundaryNone), fvarinterpboundarymethod(k_InterpolateBoundaryNone), fvarpropagatecorners(false), s_memStatsIncrement(0), s_memStatsDecrement(0), m_faceSize(sizeof(HbrFace) + 4 * ((fvarcount + 15) / 16 * sizeof(unsigned int) #ifdef HBRSTITCH + stitchCount * sizeof(StitchEdge*) #endif )), m_faceAllocator(&m_memory, 512, 0, 0, m_faceSize), m_vertexSize(sizeof(HbrVertex) + (totalfvarwidth ? (sizeof(HbrFVarData) + (totalfvarwidth - 1) * sizeof(float)) : 0)), m_vertexAllocator(&m_memory, 512, 0, 0, m_vertexSize), m_faceChildrenAllocator(&m_memory, 512, 0, 0), m_memory(0), m_numCoarseFaces(-1), hasVertexEdits(0), hasCreaseEdits(0), m_transientMode(false) { } template HbrMesh::~HbrMesh() { GarbageCollect(); int i; if (!faces.empty()) { for (i = 0; i < nfaces; ++i) { if (faces[i]) { faces[i]->Destroy(); m_faceAllocator.Deallocate(faces[i]); } } if (s_memStatsDecrement) { s_memStatsDecrement(faces.size() * sizeof(HbrFace*)); } } if (!vertices.empty()) { for (i = 0; i < nvertices; ++i) { if (vertices[i]) { vertices[i]->Destroy(this); m_vertexAllocator.Deallocate(vertices[i]); } } if (s_memStatsDecrement) { s_memStatsDecrement(vertices.size() * sizeof(HbrVertex*)); } } if (!vertexClientData.empty() && s_memStatsDecrement) { s_memStatsDecrement(vertexClientData.size() * sizeof(void*)); } if (!faceClientData.empty() && s_memStatsDecrement) { s_memStatsDecrement(faceClientData.size() * sizeof(void*)); } for (typename std::vector* >::iterator hi = hierarchicalEdits.begin(); hi != hierarchicalEdits.end(); ++hi) { delete *hi; } } template HbrVertex* HbrMesh::NewVertex(int id, const T &data) { HbrVertex* v = 0; if (nvertices <= id) { while (nvertices <= maxVertexID) { nvertices *= 2; if (nvertices < 1) nvertices = 1; } size_t oldsize = vertices.size(); vertices.resize(nvertices); if (s_memStatsIncrement) { s_memStatsIncrement((vertices.size() - oldsize) * sizeof(HbrVertex*)); } } v = vertices[id]; if (v) { v->Destroy(this); } else { v = m_vertexAllocator.Allocate(); } v->Initialize(id, data, GetTotalFVarWidth()); vertices[id] = v; if (id >= maxVertexID) { maxVertexID = id + 1; } // Newly created vertices are always candidates for garbage // collection, until they get "owned" by someone who // IncrementsUsage on the vertex. AddGarbageCollectableVertex(v); // If mesh is in transient mode, add vertex to transient list if (m_transientMode) { m_transientVertices.push_back(v); } return v; } template HbrVertex* HbrMesh::NewVertex(const T &data) { // Pick an ID - either the maximum vertex ID or a recycled ID if // we can int id = maxVertexID; if (!recycleIDs.empty()) { id = *recycleIDs.begin(); recycleIDs.erase(recycleIDs.begin()); } if (id >= maxVertexID) { maxVertexID = id + 1; } return NewVertex(id, data); } template HbrVertex* HbrMesh::NewVertex() { // Pick an ID - either the maximum vertex ID or a recycled ID if // we can int id = maxVertexID; if (!recycleIDs.empty()) { id = *recycleIDs.begin(); recycleIDs.erase(recycleIDs.begin()); } if (id >= maxVertexID) { maxVertexID = id + 1; } T data(id); data.Clear(); return NewVertex(id, data); } template HbrFace* HbrMesh::NewFace(int nv, const int *vtx, int uindex) { HbrVertex** facevertices = reinterpret_cast**>(alloca(sizeof(HbrVertex*) * nv)); int i; for (i = 0; i < nv; ++i) { facevertices[i] = GetVertex(vtx[i]); if (!facevertices[i]) { return 0; } } HbrFace *f = 0; // Resize if needed if (nfaces <= maxFaceID) { while (nfaces <= maxFaceID) { nfaces *= 2; if (nfaces < 1) nfaces = 1; } size_t oldsize = faces.size(); faces.resize(nfaces); if (s_memStatsIncrement) { s_memStatsIncrement((faces.size() - oldsize) * sizeof(HbrVertex*)); } } f = faces[maxFaceID]; if (f) { f->Destroy(); } else { f = m_faceAllocator.Allocate(); } f->Initialize(this, NULL, -1, maxFaceID, uindex, nv, facevertices, totalfvarwidth, 0); faces[maxFaceID] = f; maxFaceID++; // Update the maximum encountered uniform index if (uindex > maxUniformIndex) maxUniformIndex = uindex; // If mesh is in transient mode, add face to transient list if (m_transientMode) { m_transientFaces.push_back(f); } return f; } template HbrFace* HbrMesh::NewFace(int nv, HbrVertex **vtx, HbrFace* parent, int childindex) { HbrFace *f = 0; // Resize if needed if (nfaces <= maxFaceID) { while (nfaces <= maxFaceID) { nfaces *= 2; if (nfaces < 1) nfaces = 1; } size_t oldsize = faces.size(); faces.resize(nfaces); if (s_memStatsIncrement) { s_memStatsIncrement((faces.size() - oldsize) * sizeof(HbrVertex*)); } } f = faces[maxFaceID]; if (f) { f->Destroy(); } else { f = m_faceAllocator.Allocate(); } f->Initialize(this, parent, childindex, maxFaceID, parent ? parent->GetUniformIndex() : 0, nv, vtx, totalfvarwidth, parent ? parent->GetDepth() + 1 : 0); if (parent) { f->SetPtexIndex(parent->GetPtexIndex()); } faces[maxFaceID] = f; maxFaceID++; // If mesh is in transient mode, add face to transient list if (m_transientMode) { m_transientFaces.push_back(f); } return f; } template void HbrMesh::Finish() { int i, j; m_numCoarseFaces = 0; for (i = 0; i < nfaces; ++i) { if (faces[i]) { faces[i]->SetCoarse(); m_numCoarseFaces++; } } std::vector*> vertexlist; GetVertices(std::back_inserter(vertexlist)); for (typename std::vector*>::iterator vi = vertexlist.begin(); vi != vertexlist.end(); ++vi) { HbrVertex* vertex = *vi; if (vertex->IsConnected()) vertex->Finish(); } // Finish may have added new vertices vertexlist.clear(); GetVertices(std::back_inserter(vertexlist)); // If interpolateboundary is on, process boundary edges if (interpboundarymethod == k_InterpolateBoundaryEdgeOnly || interpboundarymethod == k_InterpolateBoundaryEdgeAndCorner) { for (i = 0; i < nfaces; ++i) { if (HbrFace* face = faces[i]) { int nv = face->GetNumVertices(); for (int k = 0; k < nv; ++k) { HbrHalfedge* edge = face->GetEdge(k); if (edge->IsBoundary()) { edge->SetSharpness(HbrHalfedge::k_InfinitelySharp); } } } } } // Process corners if (interpboundarymethod == k_InterpolateBoundaryEdgeAndCorner) { for (typename std::vector*>::iterator vi = vertexlist.begin(); vi != vertexlist.end(); ++vi) { HbrVertex* vertex = *vi; if (vertex && vertex->IsConnected() && vertex->OnBoundary() && vertex->GetCoarseValence() == 2) { vertex->SetSharpness(HbrVertex::k_InfinitelySharp); } } } // Sort the hierarchical edits if (!hierarchicalEdits.empty()) { HbrHierarchicalEditComparator cmp; int nHierarchicalEdits = (int)hierarchicalEdits.size(); std::sort(hierarchicalEdits.begin(), hierarchicalEdits.end(), cmp); // Push a sentinel null value - we rely upon this sentinel to // ensure face->GetHierarchicalEdits knows when to terminate hierarchicalEdits.push_back(0); j = 0; // Link faces to hierarchical edits for (i = 0; i < nfaces; ++i) { if (faces[i]) { while (j < nHierarchicalEdits && hierarchicalEdits[j]->GetFaceID() < i) { ++j; } if (j < nHierarchicalEdits && hierarchicalEdits[j]->GetFaceID() == i) { faces[i]->SetHierarchicalEdits(&hierarchicalEdits[j]); } } } } } template void HbrMesh::DeleteFace(HbrFace* face) { if (face->GetID() < nfaces) { HbrFace* f = faces[face->GetID()]; if (f == face) { faces[face->GetID()] = 0; face->Destroy(); m_faceAllocator.Deallocate(face); } } } template void HbrMesh::DeleteVertex(HbrVertex* vertex) { HbrVertex *v = GetVertex(vertex->GetID()); if (v == vertex) { recycleIDs.insert(vertex->GetID()); int id = vertex->GetID(); vertices[id] = 0; vertex->Destroy(this); m_vertexAllocator.Deallocate(vertex); } } template int HbrMesh::GetNumVertices() const { int count = 0; for (int i = 0; i < nvertices; ++i) { if (vertices[i]) count++; } return count; } template int HbrMesh::GetNumDisconnectedVertices() const { int disconnected = 0; for (int i = 0; i < nvertices; ++i) { if (HbrVertex* v = vertices[i]) { if (!v->IsConnected()) { disconnected++; } } } return disconnected; } template int HbrMesh::GetNumFaces() const { int count = 0; for (int i = 0; i < nfaces; ++i) { if (faces[i]) count++; } return count; } template int HbrMesh::GetNumCoarseFaces() const { // Use the value computed by Finish() if it exists if (m_numCoarseFaces >= 0) return m_numCoarseFaces; // Otherwise we have to just count it up now int count = 0; for (int i = 0; i < nfaces; ++i) { if (faces[i] && faces[i]->IsCoarse()) count++; } return count; } template HbrFace* HbrMesh::GetFace(int id) const { if (id < nfaces) { return faces[id]; } return 0; } template template void HbrMesh::GetVertices(OutputIterator lvertices) const { for (int i = 0; i < nvertices; ++i) { if (vertices[i]) *lvertices++ = vertices[i]; } } template void HbrMesh::ApplyOperatorAllVertices(HbrVertexOperator &op) const { for (int i = 0; i < nvertices; ++i) { if (vertices[i]) op(*vertices[i]); } } template template void HbrMesh::GetFaces(OutputIterator lfaces) const { for (int i = 0; i < nfaces; ++i) { if (faces[i]) *lfaces++ = faces[i]; } } template void HbrMesh::PrintStats(std::ostream &out) { int singular = 0; int sumvalence = 0; int i, nv = 0; int disconnected = 0; int extraordinary = 0; for (i = 0; i < nvertices; ++i) { if (HbrVertex* v = vertices[i]) { nv++; if (v->IsSingular()) { out << " singular: " << *v << "\n"; singular++; } else if (!v->IsConnected()) { out << " disconnected: " << *v << "\n"; disconnected++; } else { if (v->IsExtraordinary()) { extraordinary++; } sumvalence += v->GetValence(); } } } out << "Mesh has " << nv << " vertices\n"; out << "Total singular vertices " << singular << "\n"; out << "Total disconnected vertices " << disconnected << "\n"; out << "Total extraordinary vertices " << extraordinary << "\n"; out << "Average valence " << (float) sumvalence / nv << "\n"; int sumsides = 0; int numfaces = 0; for (i = 0; i < nfaces; ++i) { if (HbrFace* f = faces[i]) { numfaces++; sumsides += f->GetNumVertices(); } } out << "Mesh has " << nfaces << " faces\n"; out << "Average sidedness " << (float) sumsides / nfaces << "\n"; } template void HbrMesh::GarbageCollect() { if (gcVertices.empty()) return; static const size_t gcthreshold = 4096; if (gcVertices.size() <= gcthreshold) return; // Go through the list of garbage collectable vertices and gather // up the neighboring faces of those vertices which can be garbage // collected. std::vector*> killlist; std::vector*> vlist; // Process the vertices in the same order as they were collected // (gcVertices used to be declared as a std::deque, but that was // causing unnecessary heap traffic). int numprocessed = (int)gcVertices.size() - gcthreshold / 2; for (int i = 0; i < numprocessed; ++i) { HbrVertex* v = gcVertices[i]; v->ClearCollected(); if (v->IsUsed()) continue; vlist.push_back(v); HbrHalfedge* start = v->GetIncidentEdge(), *edge; edge = start; while (edge) { HbrFace* f = edge->GetLeftFace(); if (!f->IsCollected()) { f->SetCollected(); killlist.push_back(f); } edge = v->GetNextEdge(edge); if (edge == start) break; } } gcVertices.erase(gcVertices.begin(), gcVertices.begin() + numprocessed); // Delete those faces for (typename std::vector*>::iterator fi = killlist.begin(); fi != killlist.end(); ++fi) { if ((*fi)->GarbageCollectable()) { DeleteFace(*fi); } else { (*fi)->ClearCollected(); } } // Delete as many vertices as we can for (typename std::vector*>::iterator vi = vlist.begin(); vi != vlist.end(); ++vi) { HbrVertex* v = *vi; if (!v->IsReferenced()) { DeleteVertex(v); } } } template void HbrMesh::AddHierarchicalEdit(HbrHierarchicalEdit* edit) { hierarchicalEdits.push_back(edit); if (dynamic_cast*>(edit) || dynamic_cast*>(edit)) { hasVertexEdits = 1; } else if (dynamic_cast*>(edit)) { hasCreaseEdits = 1; } } template void HbrMesh::FreeTransientData() { // When purging transient data, we must clear the faces first for (typename std::vector*>::iterator fi = m_transientFaces.begin(); fi != m_transientFaces.end(); ++fi) { DeleteFace(*fi); } // The vertices should now be trivial to purge after the transient // faces have been cleared for (typename std::vector*>::iterator vi = m_transientVertices.begin(); vi != m_transientVertices.end(); ++vi) { DeleteVertex(*vi); } m_transientVertices.clear(); m_transientFaces.clear(); // Reset max face ID int i; for (i = nfaces - 1; i >= 0; --i) { if (faces[i]) { maxFaceID = i + 1; break; } } // Reset max vertex ID for (i = nvertices - 1; i >= 0; --i) { if (vertices[i]) { maxVertexID = i + 1; break; } } } } // end namespace OPENSUBDIV_VERSION using namespace OPENSUBDIV_VERSION; } // end namespace OpenSubdiv #endif /* HBRMESH_H */