GrpcPrint/PrintS/external/vl/include/vlGraphics/AdjacencyExtractor.hpp
2024-03-19 17:45:12 +08:00

331 lines
12 KiB
C++

/**************************************************************************************/
/* */
/* Visualization Library */
/* http://visualizationlibrary.org */
/* */
/* Copyright (c) 2005-2020, Michele Bosi */
/* All rights reserved. */
/* */
/* Redistribution and use in source and binary forms, with or without modification, */
/* are permitted provided that the following conditions are met: */
/* */
/* - Redistributions of source code must retain the above copyright notice, this */
/* list of conditions and the following disclaimer. */
/* */
/* - Redistributions in binary form must reproduce the above copyright notice, this */
/* list of conditions and the following disclaimer in the documentation and/or */
/* other materials provided with the distribution. */
/* */
/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND */
/* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED */
/* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE */
/* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR */
/* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES */
/* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; */
/* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON */
/* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */
/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */
/* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
/* */
/**************************************************************************************/
#ifndef AdjacencyExtractor_INCLUDE_ONCE
#define AdjacencyExtractor_INCLUDE_ONCE
#include <vlGraphics/link_config.hpp>
#include <vlGraphics/Geometry.hpp>
#include <vlCore/Time.hpp>
namespace vl
{
class AdjacencyExtractor {
private:
struct SEdge {
u32 A,B,O;
SEdge() {
A = B = O = 0xFFFFFFFF;
}
SEdge(u32 a, u32 b, u32 o) {
A = a;
B = b;
O = o;
}
bool isNull() const {
return A == 0xFFFFFFFF;
}
u64 id() const {
return (u64)A | ( (u64)B << 32 );
}
u64 other() const {
return (u64)B | ( (u64)A << 32 );
}
bool operator<( const SEdge& other ) const {
return id() < other.id();
}
bool operator==( const SEdge& other ) const {
return id() == other.id();
}
};
struct STriangle {
SEdge edge[3];
STriangle( u32 a, u32 b, u32 c ) {
edge[0] = SEdge(a,b,c);
edge[1] = SEdge(b,c,a);
edge[2] = SEdge(c,a,b);
}
};
/** How many collisions can SEdgeMap handle before falling back to std::map. */
static const int VL_SEdgeMap_SLOTS = 4;
struct SEdgeMapSlots {
SEdge slot[ VL_SEdgeMap_SLOTS ];
};
/** Basic hash-table implementation that falls back to std::map when too many collisions happen. */
struct SEdgeMap {
std::map<u64, SEdge> edge_map;
std::vector<SEdgeMapSlots> cache;
int cache_hits;
int edge_count;
size_t memoryUsed() const {
return cache.size() * sizeof( SEdgeMapSlots ) + edge_map.size() * ( sizeof( SEdge ) + sizeof( u64 ) );
}
SEdgeMap( int size = 104729 ) {
cache.resize( size );
cache_hits = 0;
edge_count = 0;
}
void put(u64 uid, const SEdge& edge) {
++edge_count;
VL_CHECK( ! edge.isNull() );
u64 p = uid % cache.size();
for( int i = 0; i < VL_SEdgeMap_SLOTS; ++i ) {
if ( cache[ p ].slot[ i ].isNull() ) {
cache[ p ].slot[ i ] = edge;
VL_CHECK( ! cache[ p ].slot[ i ].isNull() );
++cache_hits;
return;
}
}
edge_map[ uid ] = edge;
}
bool get(u64 uid, SEdge& edge_out) {
edge_out = SEdge();
VL_CHECK( edge_out.isNull() );
u64 p = uid % cache.size();
for( int i = 0; i < VL_SEdgeMap_SLOTS; ++i ) {
if ( ! cache[ p ].slot[ i ].isNull() && cache[ p ].slot[ i ].id() == uid ) {
edge_out = cache[ p ].slot[ i ];
return true;
}
}
std::map<u64, SEdge>::iterator it = edge_map.find( uid );
if ( it != edge_map.end() ) {
edge_out = it->second;
return true;
} else {
return false;
}
}
};
/** Implements a very fast non-resizing hash-table with a finite max number of elements (see constructor).
* Uses half the memory as SEdgeMap and is up to 2x as fast.
* Does not use std::map.
* Does not implement element deletion (not difficult to do so though).
* Does not implement hash-table resizing (doable but very expensive).
* Does not support and arbitrary number of elements (doable using maps as fallback or hash-table resizing).
*/
class SEdgeMapFast {
struct SEdgeSlot {
SEdge edge;
SEdgeSlot* next;
SEdgeSlot() {
next = NULL;
}
};
private:
std::vector<SEdgeSlot> cache;
std::vector<SEdgeSlot> free_slots;
SEdgeSlot* free_slots_head;
public:
int cache_hits;
int edge_count;
int free_slots_used;
int max_size;
public:
/** Maximum number of elements that can be stored. */
SEdgeMapFast( int size = 104729 ) {
max_size = size;
cache_hits = 0;
edge_count = 0;
free_slots_used = 0;
cache.resize( size );
free_slots.resize( size );
free_slots_head = &free_slots[0];
for( int i = 0; i < free_slots.size() - 1; ++i ) {
free_slots[i].next = &free_slots[i + 1];
}
}
size_t memoryUsed() const {
return cache.size() * sizeof( SEdgeSlot ) + free_slots.size() * sizeof( SEdgeSlot );
}
void put(u64 uid, const SEdge& edge) {
++edge_count;
u64 p = uid % cache.size();
// Find and replace
SEdgeSlot* slot = &cache[ p ];
SEdgeSlot* last = NULL;
for( ; slot != NULL; last = slot, slot = slot->next )
{
// first is free or found the edge
if( slot->edge.isNull() || slot->edge.id() == edge.id() ) {
slot->edge = edge;
if ( last == NULL) {
++cache_hits;
}
return;
}
}
// Out of memory: increase SEdgeMapFast size when constructing it.
VL_CHECK( free_slots_head );
// Get a free slot and append it to the end of the list
SEdgeSlot* free_slot = free_slots_head;
free_slots_head = free_slots_head->next;
free_slot->next = NULL;
free_slot->edge = edge;
last->next = free_slot;
++free_slots_used;
}
bool get(u64 uid, SEdge& edge_out) {
edge_out = SEdge();
u64 p = uid % cache.size();
// find
for( SEdgeSlot* slot = &cache[ p ]; slot != NULL; slot = slot->next ) {
if ( ! slot->edge.isNull() && slot->edge.id() == uid ) {
edge_out = slot->edge;
return true;
}
}
return false;
}
};
public:
static ref< Geometry > extract( Geometry* geom ) {
#ifndef NDEBUG
float t0 = Time::currentTime();
#endif
ref< Geometry > geom_adj = new Geometry;
geom_adj->setVertexArray( geom->vertexArray() );
geom_adj->setNormalArray( geom->normalArray() );
int total_triangles = 0;
for( int idc = 0; idc < geom->drawCalls().size(); ++idc ) {
int triangle_count = 0;
const DrawCall* dc = geom->drawCalls()[ idc ].get();
int indices = dc->countIndices();
SEdgeMapFast edge_map( indices );
// SEdgeMap edge_map( indices * 2 );
if ( dc->primitiveType() == vl::PT_LINES_ADJACENCY ||
dc->primitiveType() == vl::PT_LINE_STRIP_ADJACENCY ||
dc->primitiveType() == vl::PT_TRIANGLES_ADJACENCY ||
dc->primitiveType() == vl::PT_TRIANGLE_STRIP_ADJACENCY ) {
Log::error( "AdjacencyExtractor::extract(): geometry has already adjacency information." );
return NULL;
}
for( TriangleIterator trit = dc->triangleIterator(); trit.hasNext(); trit.next() )
{
u32 a = trit.a();
u32 b = trit.b();
u32 c = trit.c();
STriangle triangle( a, b, c );
edge_map.put( triangle.edge[0].id(), triangle.edge[0] );
edge_map.put( triangle.edge[1].id(), triangle.edge[1] );
edge_map.put( triangle.edge[2].id(), triangle.edge[2] );
++triangle_count;
}
total_triangles += triangle_count;
ref< DrawElementsUInt > dc_adj = new DrawElementsUInt( PT_TRIANGLES_ADJACENCY );
geom_adj->drawCalls().push_back( dc_adj.get() );
dc_adj->indexBuffer()->resize( triangle_count * 6 );
GLuint* P = dc_adj->indexBuffer()->begin();
for( TriangleIterator trit = dc->triangleIterator(); trit.hasNext(); trit.next(), P += 6 )
{
u32 a = trit.a();
u32 b = trit.b();
u32 c = trit.c();
// NOTE: degenerate edges are important for border detection.
P[0] = a;
P[1] = a; // degenerate
P[2] = b;
P[3] = b; // degenerate
P[4] = c;
P[5] = c; // degenerate
STriangle triangle( a, b, c );
SEdge edge;
if ( edge_map.get( triangle.edge[0].other(), edge ) ) {
VL_CHECK( ! edge.isNull() );
P[1] = edge.O;
}
if ( edge_map.get( triangle.edge[1].other(), edge ) ) {
VL_CHECK( ! edge.isNull() );
P[3] = edge.O;
}
if ( edge_map.get( triangle.edge[2].other(), edge ) ) {
VL_CHECK( ! edge.isNull() );
P[5] = edge.O;
}
}
#ifndef NDEBUG
printf("EdgeMap: edge-count: %d, cache-hits: %d (%.1f%%), cache MB: %.1f\n",
edge_map.edge_count, edge_map.cache_hits, 100.0f * edge_map.cache_hits / edge_map.edge_count,
edge_map.memoryUsed() / (1024.0 * 1024.0) );
#endif
}
#ifndef NDEBUG
float secs = Time::currentTime() - t0;
printf( "Adjacency Time: %.1fs, %.1fKtri/sec (%d)\n", secs, total_triangles / secs / 1000.0f, total_triangles );
#endif
return geom_adj;
}
};
}
#endif