251 lines
6.9 KiB
C++
251 lines
6.9 KiB
C++
// Copyright (C) 2007 Id Software, Inc.
|
|
//
|
|
|
|
|
|
#ifndef __VECTORWELD_H__
|
|
#define __VECTORWELD_H__
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
Vector Weld
|
|
|
|
Welds Vectors
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
template< class type, int dimension >
|
|
class idVectorWeld {
|
|
public:
|
|
idVectorWeld();
|
|
idVectorWeld( const type &mins, const type &maxs, const int boxHashSize, const int initialSize );
|
|
|
|
void Init( const type &mins, const type &maxs, const int boxHashSize, const int initialSize );
|
|
int HashSizeForBounds( const type &mins, const type &maxs, const float epsilon ) const;
|
|
void SetGranularity( int newGranularity );
|
|
void Clear();
|
|
|
|
void AddVector( type *v, const float epsilon );
|
|
int GetNumUniqueVectors() const;
|
|
void Weld( const float integralEpsilon );
|
|
|
|
private:
|
|
struct idWeldVector;
|
|
|
|
struct idWeldGroup {
|
|
idWeldVector * first; // first vector in weld group
|
|
};
|
|
struct idWeldVector {
|
|
type * v; // pointer to vector
|
|
idWeldGroup * group; // weld group this vector is in
|
|
idWeldVector * next; // next vector in the weld group
|
|
};
|
|
|
|
idList<idWeldVector *> vectors;
|
|
idList<idWeldGroup *> groups;
|
|
idHashIndex hash;
|
|
type mins;
|
|
type maxs;
|
|
int boxHashSize;
|
|
float boxInvSize[dimension];
|
|
float boxHalfSize[dimension];
|
|
|
|
idBlockAlloc<idWeldGroup,1024> weldGroupAllocator;
|
|
idBlockAlloc<idWeldVector,1024> weldVectorAllocator;
|
|
|
|
void MergeGroups( idWeldGroup *group1, idWeldGroup *group2 );
|
|
};
|
|
|
|
template< class type, int dimension >
|
|
idVectorWeld<type,dimension>::idVectorWeld() {
|
|
hash.Clear( idMath::IPow( boxHashSize, dimension ), 128 );
|
|
boxHashSize = 16;
|
|
memset( boxInvSize, 0, dimension * sizeof( boxInvSize[0] ) );
|
|
memset( boxHalfSize, 0, dimension * sizeof( boxHalfSize[0] ) );
|
|
}
|
|
|
|
template< class type, int dimension >
|
|
idVectorWeld<type,dimension>::idVectorWeld( const type &mins, const type &maxs, const int boxHashSize, const int initialSize ) {
|
|
Init( mins, maxs, boxHashSize, initialSize );
|
|
}
|
|
|
|
template< class type, int dimension >
|
|
void idVectorWeld<type,dimension>::Init( const type &mins, const type &maxs, const int boxHashSize, const int initialSize ) {
|
|
int i;
|
|
float boxSize;
|
|
|
|
vectors.AssureSize( initialSize );
|
|
vectors.SetNum( 0, false );
|
|
vectors.SetGranularity( 1024 );
|
|
|
|
groups.AssureSize( initialSize );
|
|
groups.SetNum( 0, false );
|
|
groups.SetGranularity( 1024 );
|
|
|
|
hash.Clear( idMath::IPow( boxHashSize, dimension ), initialSize );
|
|
hash.SetGranularity( 1024 );
|
|
|
|
this->mins = mins;
|
|
this->maxs = maxs;
|
|
this->boxHashSize = boxHashSize;
|
|
|
|
for ( i = 0; i < dimension; i++ ) {
|
|
boxSize = ( maxs[i] - mins[i] ) / (float) boxHashSize;
|
|
boxInvSize[i] = 1.0f / boxSize;
|
|
boxHalfSize[i] = boxSize * 0.5f;
|
|
}
|
|
}
|
|
|
|
template< class type, int dimension >
|
|
int idVectorWeld<type,dimension>::HashSizeForBounds( const type &mins, const type &maxs, const float epsilon ) const {
|
|
int i;
|
|
float min = idMath::INFINITY;
|
|
for ( i = 0; i < 3; i++ ) {
|
|
if ( maxs[i] - mins[i] < min ) {
|
|
min = maxs[i] - mins[i];
|
|
}
|
|
}
|
|
min /= epsilon * 4;
|
|
for ( i = 0; i < 7; i++ ) {
|
|
if ( ( 1 << i ) > min ) {
|
|
break;
|
|
}
|
|
}
|
|
return 1 << i;
|
|
}
|
|
|
|
template< class type, int dimension >
|
|
void idVectorWeld<type,dimension>::SetGranularity( int newGranularity ) {
|
|
vectors.SetGranularity( newGranularity );
|
|
groups.SetGranularity( newGranularity );
|
|
hash.SetGranularity( newGranularity );
|
|
}
|
|
|
|
template< class type, int dimension >
|
|
void idVectorWeld<type,dimension>::Clear() {
|
|
vectors.Clear();
|
|
groups.Clear();
|
|
hash.Clear();
|
|
}
|
|
|
|
template< class type, int dimension >
|
|
void idVectorWeld<type,dimension>::AddVector( type *v, const float epsilon ) {
|
|
int i, j, k, hashKey, partialHashKey[dimension];
|
|
|
|
idWeldVector *vector = weldVectorAllocator.Alloc();
|
|
vector->group = weldGroupAllocator.Alloc();
|
|
vector->group->first = vector;
|
|
vector->v = v;
|
|
vector->next = NULL;
|
|
|
|
vectors.Alloc() = vector;
|
|
groups.Alloc() = vector->group;
|
|
|
|
// create partial hash key for this vector
|
|
for ( i = 0; i < dimension; i++ ) {
|
|
assert( epsilon <= boxHalfSize[i] );
|
|
partialHashKey[i] = idMath::Ftoi( ( (*vector->v)[i] - mins[i] - boxHalfSize[i] ) * boxInvSize[i] );
|
|
}
|
|
|
|
// find nearby vectors and merge their groups into the group of this vector
|
|
for ( i = 0; i < ( 1 << dimension ); i++ ) {
|
|
|
|
hashKey = 0;
|
|
for ( j = 0; j < dimension; j++ ) {
|
|
hashKey *= boxHashSize;
|
|
hashKey += partialHashKey[j] + ( ( i >> j ) & 1 );
|
|
}
|
|
|
|
for ( j = hash.GetFirst( hashKey ); j != idHashIndex::NULL_INDEX; j = hash.GetNext( j ) ) {
|
|
// if already in the same group
|
|
if ( vectors[j]->group == vector->group ) {
|
|
continue;
|
|
}
|
|
|
|
// merge groups if the vertices are close enough to each other
|
|
const type &v1 = *vector->v;
|
|
const type &v2 = *vectors[j]->v;
|
|
for ( k = 0; k < dimension; k++ ) {
|
|
if ( idMath::Fabs( v1[k] - v2[k] ) > epsilon ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( k >= dimension ) {
|
|
MergeGroups( vector->group, vectors[j]->group );
|
|
}
|
|
}
|
|
}
|
|
|
|
// add the vector to the hash
|
|
hashKey = 0;
|
|
for ( i = 0; i < dimension; i++ ) {
|
|
hashKey *= boxHashSize;
|
|
hashKey += idMath::Ftoi( ( (*vector->v)[i] - mins[i] ) * boxInvSize[i] );
|
|
}
|
|
|
|
hash.Add( hashKey, vectors.Num() - 1 );
|
|
}
|
|
|
|
template< class type, int dimension >
|
|
int idVectorWeld<type,dimension>::GetNumUniqueVectors() const {
|
|
return groups.Num();
|
|
}
|
|
|
|
template< class type, int dimension >
|
|
void idVectorWeld<type,dimension>::Weld( const float integralEpsilon ) {
|
|
int i, j;
|
|
idWeldVector *vector;
|
|
type mins, maxs, center;
|
|
|
|
for ( i = 0; i < groups.Num(); i++ ) {
|
|
if ( groups[i]->first == NULL ) {
|
|
continue;
|
|
}
|
|
for ( j = 0; j < dimension; j++ ) {
|
|
mins[j] = idMath::INFINITY;
|
|
maxs[j] = -idMath::INFINITY;
|
|
}
|
|
for ( vector = groups[i]->first; vector != NULL; vector = vector->next ) {
|
|
const type &v = *vector->v;
|
|
for ( j = 0; j < dimension; j++ ) {
|
|
if ( v[j] < mins[j] ) {
|
|
mins[j] = v[j];
|
|
}
|
|
if ( v[j] > maxs[j] ) {
|
|
maxs[j] = v[j];
|
|
}
|
|
}
|
|
}
|
|
center = ( mins + maxs ) * 0.5f;
|
|
for ( j = 0; j < dimension; j++ ) {
|
|
if ( idMath::Fabs( center[j] - idMath::Rint( center[j] ) ) < integralEpsilon ) {
|
|
center[j] = idMath::Rint( center[j] );
|
|
}
|
|
}
|
|
for ( vector = groups[i]->first; vector != NULL; vector = vector->next ) {
|
|
*vector->v = center;
|
|
}
|
|
}
|
|
}
|
|
|
|
template< class type, int dimension >
|
|
void idVectorWeld<type,dimension>::MergeGroups( idWeldGroup *group1, idWeldGroup *group2 ) {
|
|
idWeldVector *v, *next;
|
|
|
|
// move all vertices from group2 to group1
|
|
for ( v = group2->first; v != NULL; v = next ) {
|
|
next = v->next;
|
|
v->group = group1;
|
|
v->next = group1->first;
|
|
group1->first = v;
|
|
}
|
|
|
|
// remove group2
|
|
group2->first = NULL;
|
|
groups.RemoveFast( group2 );
|
|
weldGroupAllocator.Free( group2 );
|
|
}
|
|
|
|
#endif /* !__VECTORWELD_H__ */
|