Skip to content

Commit

Permalink
Optimize progressive mesh peformance from O(n * n) -> O(n * log n) (#239
Browse files Browse the repository at this point in the history
)
  • Loading branch information
T-rvw authored Oct 7, 2023
1 parent 5dc22fc commit 50058cb
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 13 deletions.
7 changes: 3 additions & 4 deletions examples/Prototype/ProgressiveMesh/GenericToPM/Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ int main(int argc, char** argv)
uint32_t polygonCount = mesh.GetPolygonCount();

std::vector<uint32_t> indexBuffer;
indexBuffer.reserve(polygonCount);
for (uint32_t polygonIndex = 0U; polygonIndex < polygonCount; ++polygonIndex)
{
const auto& polygon = mesh.GetPolygon(polygonIndex);
Expand All @@ -54,10 +53,10 @@ int main(int argc, char** argv)
}
}

auto hem = cd::HalfEdgeMesh::FromIndexedMesh(mesh);
auto boundaryMesh = cd::Mesh::FromHalfEdgeMesh(hem, cd::ConvertStrategy::BoundaryOnly);
//auto hem = cd::HalfEdgeMesh::FromIndexedMesh(mesh);
//auto boundaryMesh = cd::Mesh::FromHalfEdgeMesh(hem, cd::ConvertStrategy::BoundaryOnly);
auto pm = cd::ProgressiveMesh::FromIndexedMesh(mesh);
pm.InitBoundary(boundaryMesh);
//pm.InitBoundary(boundaryMesh);
auto [permutation, map] = pm.BuildCollapseOperations();
}

Expand Down
27 changes: 20 additions & 7 deletions private/ProgressiveMesh/ProgressiveMeshImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "Scene/Mesh.h"

#include <cfloat>
#include <unordered_map>

namespace cd::pm
{
Expand Down Expand Up @@ -79,6 +80,12 @@ std::pair<std::vector<uint32_t>, std::vector<uint32_t>> ProgressiveMeshImpl::Bui
ComputeEdgeCollapseCostAtVertex(vertex.GetID());
}

for (auto& vertex : m_vertices)
{
assert(vertex.GetID().IsValid());
m_minCostVertexQueue.insert(&vertex);
}

uint32_t vertexCount = GetVertexCount();
std::vector<uint32_t> permutation;
permutation.resize(vertexCount);
Expand All @@ -88,12 +95,16 @@ std::pair<std::vector<uint32_t>, std::vector<uint32_t>> ProgressiveMeshImpl::Bui

for (int vertexIndex = static_cast<int>(vertexCount) - 1; vertexIndex >= 0; --vertexIndex)
{
Vertex* pCandidate = GetMinimumCostVertex();
assert(!m_minCostVertexQueue.empty());
auto itMinVertex = m_minCostVertexQueue.begin();
Vertex* pCandidate = *itMinVertex;
m_minCostVertexQueue.erase(itMinVertex);

assert(pCandidate);
permutation[pCandidate->GetID().Data()] = vertexIndex;
map[vertexIndex] = pCandidate->GetCollapseTarget().Data();

printf("Collapse2 [Vertex %d] - [Vertex %d]\n", pCandidate->GetID().Data(), pCandidate->GetCollapseTarget().Data());
//printf("Collapse [Vertex %d] - [Vertex %d], cost = %f\n", pCandidate->GetID().Data(), pCandidate->GetCollapseTarget().Data(), pCandidate->GetCollapseCost());
Collapse(pCandidate->GetID(), pCandidate->GetCollapseTarget());
}

Expand Down Expand Up @@ -314,11 +325,10 @@ float ProgressiveMeshImpl::ComputeEdgeCollapseCostAtEdge(VertexID v0ID, VertexID
}

float cost = edgeLength * curvature;
if (v0.IsOnBoundary())
{
cost += 1.0f;
}

//if (v0.IsOnBoundary())
//{
// cost += 1.0f;
//}
return cost;
}

Expand Down Expand Up @@ -376,8 +386,11 @@ void ProgressiveMeshImpl::Collapse(VertexID v0ID, VertexID v1ID)

for (auto vertexID : tmp)
{
Vertex& v = GetVertex(vertexID.Data());
m_minCostVertexQueue.erase(&v);
//printf("\t2 ComputeEdgeCostAtVertex [%d]\n", vertexID.Data());
ComputeEdgeCollapseCostAtVertex(vertexID);
m_minCostVertexQueue.insert(&v);
}
}

Expand Down
16 changes: 15 additions & 1 deletion private/ProgressiveMesh/ProgressiveMeshImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include "Vertex.h"
#include "Scene/ObjectID.h"

#include <unordered_map>
#include <set>

namespace cd
{
Expand All @@ -14,6 +14,19 @@ class Mesh;
namespace pm
{

struct CompareVertexCollapseCost
{
bool operator()(const Vertex* pV0, const Vertex* pV1) const
{
float v0Cost = pV0->GetCollapseCost();
float v1Cost = pV1->GetCollapseCost();
//printf("Compare [Vertex %u] and [Vertex %u]\n", pV0->GetID().Data(), pV1->GetID().Data());
//printf("\t[Vertex %u] cost is %f\n", pV0->GetID().Data(), v0Cost);
//printf("\t[Vertex %u] cost is %f\n", pV1->GetID().Data(), v1Cost);
return v0Cost == v1Cost ? pV0->GetID() < pV1->GetID() : v0Cost < v1Cost;
}
};

class CORE_API ProgressiveMeshImpl
{
public:
Expand Down Expand Up @@ -51,6 +64,7 @@ class CORE_API ProgressiveMeshImpl
private:
std::vector<Vertex> m_vertices;
std::vector<Face> m_faces;
std::multiset<Vertex*, CompareVertexCollapseCost> m_minCostVertexQueue;
};

}
Expand Down
5 changes: 5 additions & 0 deletions private/ProgressiveMesh/Vertex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,9 @@ void Vertex::AddAdjacentFace(FaceID faceID)
}
}

float Vertex::GetCollapseCost() const
{
return m_collapseCost;
}

}
2 changes: 1 addition & 1 deletion private/ProgressiveMesh/Vertex.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class Vertex
const cd::DynamicArray<cd::FaceID>& GetAdjacentFaces() const { return m_adjacentFaces; }

void SetCollapseCost(float cost) { m_collapseCost = cost; }
float GetCollapseCost() const { return m_collapseCost; }
float GetCollapseCost() const;

void SetCollapseTarget(cd::VertexID target) { m_collapseTarget = target; }
cd::VertexID GetCollapseTarget() const { return m_collapseTarget; }
Expand Down
37 changes: 37 additions & 0 deletions public/Container/IterablePriorityQueueProxy.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#pragma once

#include <concepts>
#include <queue>

namespace cd
{

// IterablePriorityQueueProxy is a helper to iterate elements in std::priority_queue which mostly is used to debug.
// In C++, the best priority queue I think is std::multiset which supports update operation. (Just erase and insert again!)
// Emmmm, std::priority is nothing but a wrapper of std::make_heap/push_heap/pop_heap.
template<typename T>
concept use_vector_container = requires(T)
{
{std::vector<typename T::value_type>{}} -> std::same_as<typename T::container_type>;
};

template<typename T> requires use_vector_container<T>
class IterablePriorityQueueProxy
{
public:
IterablePriorityQueueProxy() = delete;
IterablePriorityQueueProxy(const T& pq) : m_pPriorityQueue(&pq) {}
IterablePriorityQueueProxy(const IterablePriorityQueueProxy&) = default;
IterablePriorityQueueProxy& operator=(const IterablePriorityQueueProxy&) = default;
IterablePriorityQueueProxy(IterablePriorityQueueProxy&&) = default;
IterablePriorityQueueProxy& operator=(IterablePriorityQueueProxy&&) = default;
~IterablePriorityQueueProxy() = default;

auto begin() const { return &m_pPriorityQueue->top(); };
auto end() const { return begin() + m_pPriorityQueue->size(); };

private:
const T* m_pPriorityQueue = nullptr;
};

}

0 comments on commit 50058cb

Please sign in to comment.