Skip to content

Commit 28f73f5

Browse files
author
kevyuu
committed
Refactor smooth normal generator to use VertexHashGrid and separate weldVertices into CVertexWelder
1 parent ce99287 commit 28f73f5

File tree

10 files changed

+288
-534
lines changed

10 files changed

+288
-534
lines changed

include/nbl/asset/utils/CPolygonGeometryManipulator.h

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
#include "nbl/asset/ICPUPolygonGeometry.h"
1111
#include "nbl/asset/utils/CGeometryManipulator.h"
12-
#include "nbl/asset/utils/CVertexHashMap.h"
12+
#include "nbl/asset/utils/CVertexHashGrid.h"
1313

1414
namespace nbl::asset
1515
{
@@ -19,7 +19,30 @@ class NBL_API2 CPolygonGeometryManipulator
1919
{
2020
public:
2121

22-
using SSNGVertexData = CVertexHashMap::VertexData;
22+
struct SSNGVertexData
23+
{
24+
uint64_t index; //offset of the vertex into index buffer
25+
uint32_t hash;
26+
hlsl::float32_t3 weightedNormal;
27+
// TODO(kevinyu): Should we separate this from SSNGVertexData, and store it in its own vector in VertexHashGrid? Similar like how hashmap work. Or keep it intrusive?
28+
hlsl::float32_t3 position; //position of the vertex in 3D space
29+
30+
hlsl::float32_t3 getPosition() const
31+
{
32+
return position;
33+
}
34+
35+
void setHash(uint32_t hash)
36+
{
37+
this->hash = hash;
38+
}
39+
40+
uint32_t getHash() const
41+
{
42+
return hash;
43+
};
44+
45+
};
2346

2447
using VxCmpFunction = std::function<bool(const SSNGVertexData&, const SSNGVertexData&, const ICPUPolygonGeometry*)>;
2548

include/nbl/asset/utils/CVertexHashMap.h

Lines changed: 0 additions & 87 deletions
This file was deleted.

include/nbl/asset/utils/CVertexWelder.h

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,124 @@ namespace nbl::asset {
1010

1111
class CVertexWelder {
1212

13+
public:
14+
using WeldPredicateFn = std::function<bool(const ICPUPolygonGeometry* geom, uint64_t idx1, uint64_t idx2)>;
15+
1316
template <typename AccelStructureT>
14-
static core::smart_refctd_ptr<ICPUPolygonGeometry> weldVertices(const ICPUPolygonGeometry* polygon, const AccelStructureT& vertices, float epsilon);
17+
static core::smart_refctd_ptr<ICPUPolygonGeometry> weldVertices(const ICPUPolygonGeometry* polygon, const AccelStructureT& as, WeldPredicateFn shouldWeldFn) {
18+
auto outPolygon = core::move_and_static_cast<ICPUPolygonGeometry>(polygon->clone(0u));
19+
outPolygon->setIndexing(IPolygonGeometryBase::TriangleList());
20+
21+
core::vector<uint64_t> vertexIndexToAsIndex(as.getVertexCount());
22+
23+
for (uint64_t vertexData_i = 0u; vertexData_i < as.getVertexCount(); vertexData_i++)
24+
{
25+
const auto& vertexData = as.vertices()[vertexData_i];
26+
vertexIndexToAsIndex[vertexData.index] = vertexData.index;
27+
}
28+
29+
static constexpr auto INVALID_INDEX = std::numeric_limits<uint64_t>::max();
30+
core::vector<uint64_t> remappedVertexIndexes(as.getVertexCount());
31+
std::fill(remappedVertexIndexes.begin(), remappedVertexIndexes.end(), INVALID_INDEX);
32+
33+
uint64_t maxRemappedIndex = 0;
34+
// iterate by index, so that we always use the smallest index when multiple vertexes can be welded together
35+
for (uint64_t index = 0; index < as.getVertexCount(); index++)
36+
{
37+
const auto asIndex = vertexIndexToAsIndex[index];
38+
const auto& vertexData = as.vertices()[asIndex];
39+
auto& remappedVertexIndex = remappedVertexIndexes[index];
40+
as.iterateBroadphaseCandidates(vertexData, [&, polygon, index](const typename AccelStructureT::vertex_data_t& neighbor) {
41+
const auto neighborRemappedIndex = remappedVertexIndexes[neighbor.index];
42+
if (shouldWeldFn(polygon, index, neighbor.index) && neighborRemappedIndex != INVALID_INDEX) {
43+
remappedVertexIndex = neighborRemappedIndex;
44+
return false;
45+
}
46+
return true;
47+
});
48+
if (remappedVertexIndex != INVALID_INDEX) {
49+
remappedVertexIndex = vertexData.index;
50+
maxRemappedIndex = vertexData.index;
51+
}
52+
}
53+
54+
// TODO(kevinyu): Handle when indexBuffer is not exist
55+
56+
const auto& indexView = outPolygon->getIndexView();
57+
if (indexView)
58+
{
59+
auto remappedIndexView = [&]
60+
{
61+
const auto bytesize = indexView.src.size;
62+
auto indices = ICPUBuffer::create({bytesize,IBuffer::EUF_INDEX_BUFFER_BIT});
63+
64+
auto retval = indexView;
65+
retval.src.buffer = std::move(indices);
66+
if (retval.composed.rangeFormat == IGeometryBase::EAABBFormat::U16)
67+
retval.composed.encodedDataRange.u16.maxVx[0] = maxRemappedIndex;
68+
else if (retval.composed.rangeFormat == IGeometryBase::EAABBFormat::U32)
69+
retval.composed.encodedDataRange.u32.maxVx[0] = maxRemappedIndex;
70+
71+
return retval;
72+
}();
73+
74+
75+
auto remappedIndexes = [&]<typename IndexT>() {
76+
auto* indexPtr = reinterpret_cast<IndexT*>(remappedIndexView.getPointer());
77+
for (uint64_t index_i = 0; index_i < polygon->getIndexCount(); index_i++)
78+
{
79+
hlsl::vector<IndexT, 1> index;
80+
indexView.decodeElement<hlsl::vector<IndexT, 1>>(index_i, index);
81+
IndexT remappedIndex = remappedVertexIndexes[index.x];
82+
indexPtr[index_i] = remappedIndex;
83+
}
84+
};
85+
86+
if (indexView.composed.rangeFormat == IGeometryBase::EAABBFormat::U16) {
87+
remappedIndexes.template operator()<uint16_t>();
88+
}
89+
else if (indexView.composed.rangeFormat == IGeometryBase::EAABBFormat::U32) {
90+
remappedIndexes.template operator()<uint32_t>();
91+
}
92+
93+
outPolygon->setIndexView(std::move(remappedIndexView));
94+
} else
95+
{
96+
const uint32_t indexSize = (outPolygon->getPositionView().getElementCount() - 1 < std::numeric_limits<uint16_t>::max()) ? sizeof(uint16_t) : sizeof(uint32_t);
97+
auto remappedIndexBuffer = ICPUBuffer::create({indexSize * outPolygon->getVertexReferenceCount(), IBuffer::EUF_INDEX_BUFFER_BIT});
98+
auto remappedIndexView = ICPUPolygonGeometry::SDataView{
99+
.composed = {
100+
.stride = indexSize,
101+
},
102+
.src = {
103+
.offset = 0,
104+
.size = remappedIndexBuffer->getSize(),
105+
.buffer = std::move(remappedIndexBuffer)
106+
}
107+
};
108+
109+
auto fillRemappedIndex = [&]<typename IndexT>(){
110+
auto remappedIndexBufferPtr = reinterpret_cast<IndexT*>(remappedIndexBuffer->getPointer());
111+
for (uint64_t index = 0; index < outPolygon->getPositionView().getElementCount(); index++)
112+
{
113+
remappedIndexBufferPtr[index] = remappedVertexIndexes[index];
114+
}
115+
};
116+
117+
if (indexView.composed.rangeFormat == IGeometryBase::EAABBFormat::U16) {
118+
fillRemappedIndex.template operator()<uint16_t>();
119+
}
120+
else if (indexView.composed.rangeFormat == IGeometryBase::EAABBFormat::U32) {
121+
fillRemappedIndex.template operator()<uint32_t>();
122+
}
123+
124+
outPolygon->setIndexView(std::move(remappedIndexView));
125+
126+
}
127+
128+
CPolygonGeometryManipulator::recomputeContentHashes(outPolygon.get());
129+
return outPolygon;
130+
}
15131
};
16132

17133
}

include/nbl/core/algorithm/radix_sort.h

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ struct LSBSorter
7171
{
7272
constexpr histogram_t shift = static_cast<histogram_t>(radix_bits * last_pass);
7373
const auto histogramIx = (key >> shift) & radix_mask;
74-
return { histogram[histogramIx], histogram[histogramIx + 1] };
74+
const auto boundBegin = histogramIx == 0 ? 0 : histogram[histogramIx - 1];
75+
return { boundBegin, histogram[histogramIx] };
7576
}
7677

7778
private:
@@ -82,21 +83,41 @@ struct LSBSorter
8283
std::fill_n(histogram,histogram_size,static_cast<histogram_t>(0u));
8384
// count
8485
constexpr histogram_t shift = static_cast<histogram_t>(radix_bits*pass_ix);
85-
for (histogram_t i=0u; i<rangeSize; i++)
86+
for (histogram_t i = 0u; i < rangeSize; i++)
8687
++histogram[comp.template operator()<shift,radix_mask>(input[i])];
8788
// prefix sum
88-
std::inclusive_scan(histogram,histogram+histogram_size,histogram);
89+
std::inclusive_scan(histogram, histogram + histogram_size, histogram);
8990
// scatter
90-
for (histogram_t i=rangeSize; i!=0u;)
91-
{
92-
i--;
93-
output[--histogram[comp.template operator()<shift,radix_mask>(input[i])]] = input[i];
94-
}
9591

9692
if constexpr (pass_ix != last_pass)
93+
{
94+
95+
for (histogram_t i = rangeSize; i != 0u;)
96+
{
97+
i--;
98+
const auto& val = input[i];
99+
const auto& histogramIx = comp.template operator()<shift,radix_mask>(val);
100+
output[--histogram[histogramIx]] = val;
101+
}
102+
97103
return pass<RandomIt,KeyAccessor,pass_ix+1ull>(output,input,rangeSize,comp);
104+
}
98105
else
106+
{
107+
// need to preserve histogram value for the skip list, so we copy to temporary histogramArray and use that
108+
std::array<histogram_t, histogram_size> tmpHistogram;
109+
std::copy(histogram, histogram + histogram_size, tmpHistogram.data());
110+
111+
for (histogram_t i = rangeSize; i != 0u;)
112+
{
113+
i--;
114+
const auto& val = input[i];
115+
const auto& histogramIx = comp.template operator()<shift,radix_mask>(val);
116+
output[--tmpHistogram[histogramIx]] = val;
117+
}
118+
99119
return output;
120+
}
100121
}
101122

102123
alignas(sizeof(histogram_t)) histogram_t histogram[histogram_size];

src/nbl/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,6 @@ set(NBL_ASSET_SOURCES
184184
asset/utils/CPolygonGeometryManipulator.cpp
185185
asset/utils/COverdrawPolygonGeometryOptimizer.cpp
186186
asset/utils/CSmoothNormalGenerator.cpp
187-
asset/utils/CVertexHashMap.cpp
188187

189188
# Mesh loaders
190189
asset/interchange/COBJMeshFileLoader.cpp

src/nbl/asset/utils/CGeometryCreator.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ template <typename IndexT>
8383
requires(std::is_same_v<IndexT, uint16_t> || std::is_same_v<IndexT, uint32_t>)
8484
static ICPUPolygonGeometry::SDataView createIndexView(size_t indexCount, size_t maxIndex)
8585
{
86-
8786
const auto bytesize = sizeof(IndexT) * indexCount;
8887
auto indices = ICPUBuffer::create({bytesize,IBuffer::EUF_INDEX_BUFFER_BIT});
8988

0 commit comments

Comments
 (0)