1+ #ifndef _NBL_ASSET_C_VERTEX_HASH_MAP_H_INCLUDED_
2+ #define _NBL_ASSET_C_VERTEX_HASH_MAP_H_INCLUDED_
3+
4+ #include " nbl/core/declarations.h"
5+
6+ namespace nbl ::asset
7+ {
8+
9+ template <typename T>
10+ concept HashGridVertexData = requires (T obj, T const cobj, uint32_t hash) {
11+ { cobj.getHash () } -> std::same_as<uint32_t >;
12+ { obj.setHash (hash) } -> std::same_as<void >;
13+ { cobj.getPosition () } -> std::same_as<hlsl::float32_t3>;
14+ };
15+
16+ template <HashGridVertexData VertexData>
17+ class CVertexHashGrid
18+ {
19+ public:
20+
21+ using vertex_data_t = VertexData;
22+ using collection_t = core::vector<VertexData>;
23+ struct BucketBounds
24+ {
25+ collection_t ::const_iterator begin;
26+ collection_t ::const_iterator end;
27+ };
28+
29+ CVertexHashGrid (size_t _vertexCount, uint32_t _hashTableMaxSize, float _cellSize) :
30+ m_sorter (createSorter(_vertexCount)),
31+ m_hashTableMaxSize (_hashTableMaxSize),
32+ m_cellSize (_cellSize)
33+ {
34+ assert ((core::isPoT (m_hashTableMaxSize)));
35+
36+ m_vertices.reserve (_vertexCount);
37+ }
38+
39+ // inserts vertex into hash table
40+ void add (VertexData&& vertex)
41+ {
42+ vertex.setHash (hash (vertex));
43+ m_vertices.push_back (vertex);
44+ }
45+
46+ void validate ()
47+ {
48+ const auto oldSize = m_vertices.size ();
49+ m_vertices.resize (oldSize*2u );
50+ auto finalSortedOutput = std::visit ( [&](auto & sorter) { return sorter (m_vertices.data (), m_vertices.data () + oldSize, oldSize, KeyAccessor ()); },m_sorter );
51+
52+ if (finalSortedOutput != m_vertices.data ())
53+ m_vertices.erase (m_vertices.begin (), m_vertices.begin () + oldSize);
54+ else
55+ m_vertices.resize (oldSize);
56+ }
57+
58+ const collection_t & vertices () const { return m_vertices; }
59+
60+ collection_t & vertices (){ return m_vertices; }
61+
62+ inline uint32_t getVertexCount () const { return m_vertices.size (); }
63+
64+ template <typename Fn>
65+ void iterateBroadphaseCandidates (const VertexData& vertex, Fn fn) const
66+ {
67+ std::array<uint32_t , 8 > neighboringCells;
68+ const auto cellCount = getNeighboringCellHashes (neighboringCells.data (), vertex);
69+
70+ // iterate among all neighboring cells
71+ for (uint8_t i = 0 ; i < cellCount; i++)
72+ {
73+ const auto & neighborCell = neighboringCells[i];
74+ BucketBounds bounds = getBucketBoundsByHash (neighborCell);
75+ for (; bounds.begin != bounds.end ; bounds.begin ++)
76+ {
77+ const vertex_data_t & neighborVertex = *bounds.begin ;
78+ if (&vertex != &neighborVertex)
79+ if (!fn (neighborVertex)) break ;
80+ }
81+ }
82+
83+ };
84+
85+ private:
86+ struct KeyAccessor
87+ {
88+ _NBL_STATIC_INLINE_CONSTEXPR size_t key_bit_count = 32ull ;
89+
90+ template <auto bit_offset, auto radix_mask>
91+ inline decltype (radix_mask) operator()(const VertexData& item) const
92+ {
93+ return static_cast <decltype (radix_mask)>(item.getHash () >> static_cast <uint32_t >(bit_offset)) & radix_mask;
94+ }
95+ };
96+
97+ static constexpr uint32_t primeNumber1 = 73856093 ;
98+ static constexpr uint32_t primeNumber2 = 19349663 ;
99+ static constexpr uint32_t primeNumber3 = 83492791 ;
100+
101+ static constexpr uint32_t invalidHash = 0xFFFFFFFF ;
102+
103+ using sorter_t = std::variant<
104+ core::LSBSorter<KeyAccessor::key_bit_count, uint16_t >,
105+ core::LSBSorter<KeyAccessor::key_bit_count, uint32_t >,
106+ core::LSBSorter<KeyAccessor::key_bit_count, size_t >>;
107+ sorter_t m_sorter;
108+
109+ static sorter_t createSorter (size_t vertexCount)
110+ {
111+ if (vertexCount < (0x1ull << 16ull ))
112+ return core::LSBSorter<KeyAccessor::key_bit_count, uint16_t >();
113+ if (vertexCount < (0x1ull << 32ull ))
114+ return core::LSBSorter<KeyAccessor::key_bit_count, uint32_t >();
115+ return core::LSBSorter<KeyAccessor::key_bit_count, size_t >();
116+ }
117+
118+ collection_t m_vertices;
119+ const uint32_t m_hashTableMaxSize;
120+ const float m_cellSize;
121+
122+ uint32_t hash (const VertexData& vertex) const
123+ {
124+ const hlsl::float32_t3 position = floor (vertex.getPosition () / m_cellSize);
125+
126+ return ((static_cast <uint32_t >(position.x ) * primeNumber1) ^
127+ (static_cast <uint32_t >(position.y ) * primeNumber2) ^
128+ (static_cast <uint32_t >(position.z ) * primeNumber3))& (m_hashTableMaxSize - 1 );
129+ }
130+
131+ uint32_t hash (const hlsl::uint32_t3& position) const
132+ {
133+ return ((position.x * primeNumber1) ^
134+ (position.y * primeNumber2) ^
135+ (position.z * primeNumber3))& (m_hashTableMaxSize - 1 );
136+ }
137+
138+ uint8_t getNeighboringCellHashes (uint32_t * outNeighbors, const VertexData& vertex) const
139+ {
140+ hlsl::float32_t3 cellfloatcoord = floor (vertex.getPosition () / m_cellSize - hlsl::float32_t3 (0.5 ));
141+ hlsl::uint32_t3 baseCoord = hlsl::uint32_t3 (static_cast <uint32_t >(cellfloatcoord.x ), static_cast <uint32_t >(cellfloatcoord.y ), static_cast <uint32_t >(cellfloatcoord.z ));
142+
143+ uint8_t neighborCount = 0 ;
144+
145+ outNeighbors[neighborCount] = hash (baseCoord);
146+ neighborCount++;
147+
148+ auto addUniqueNeighbor = [&neighborCount, outNeighbors](uint32_t hashval)
149+ {
150+ if (std::find (outNeighbors, outNeighbors + neighborCount, hashval) == outNeighbors + neighborCount)
151+ {
152+ outNeighbors[neighborCount] = hashval;
153+ neighborCount++;
154+ }
155+ };
156+
157+ addUniqueNeighbor (hash (baseCoord + hlsl::uint32_t3 (0 , 0 , 1 )));
158+ addUniqueNeighbor (hash (baseCoord + hlsl::uint32_t3 (0 , 1 , 0 )));
159+ addUniqueNeighbor (hash (baseCoord + hlsl::uint32_t3 (1 , 0 , 0 )));
160+ addUniqueNeighbor (hash (baseCoord + hlsl::uint32_t3 (1 , 1 , 0 )));
161+ addUniqueNeighbor (hash (baseCoord + hlsl::uint32_t3 (1 , 0 , 1 )));
162+ addUniqueNeighbor (hash (baseCoord + hlsl::uint32_t3 (0 , 1 , 1 )));
163+ addUniqueNeighbor (hash (baseCoord + hlsl::uint32_t3 (1 , 1 , 1 )));
164+
165+ return neighborCount;
166+ }
167+
168+ BucketBounds getBucketBoundsByHash (uint32_t hash) const
169+ {
170+ if (hash == invalidHash)
171+ return { m_vertices.end (), m_vertices.end () };
172+
173+ const auto skipListBound = std::visit ([&](auto & sorter)
174+ {
175+ auto hashBound = sorter.getHashBound (hash);
176+ return std::pair<collection_t ::const_iterator, collection_t ::const_iterator>(m_vertices.begin () + hashBound.first , m_vertices.begin () + hashBound.second );
177+ }, m_sorter);
178+
179+ auto begin = std::lower_bound (
180+ skipListBound.first ,
181+ skipListBound.second ,
182+ hash,
183+ [](const VertexData& vertex, uint32_t hash)
184+ {
185+ return vertex.hash < hash;
186+ });
187+
188+ auto end = std::upper_bound (
189+ skipListBound.first ,
190+ skipListBound.second ,
191+ hash,
192+ [](uint32_t hash, const VertexData& vertex)
193+ {
194+ return hash < vertex.hash ;
195+ });
196+
197+ const auto beginIx = begin - m_vertices.begin ();
198+ const auto endIx = end - m_vertices.begin ();
199+ // bucket missing
200+ if (begin == end)
201+ return { m_vertices.end (), m_vertices.end () };
202+
203+ // bucket missing
204+ if (begin->hash != hash)
205+ return { m_vertices.end (), m_vertices.end () };
206+
207+ return { begin, end };
208+ }
209+ };
210+
211+ }
212+ #endif
0 commit comments