Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 5 additions & 13 deletions include/nbl/asset/utils/CPolygonGeometryManipulator.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

#include "nbl/asset/ICPUPolygonGeometry.h"
#include "nbl/asset/utils/CGeometryManipulator.h"
#include "nbl/asset/utils/CSmoothNormalGenerator.h"
#include "nbl/asset/utils/CVertexHashGrid.h"

namespace nbl::asset
{
Expand All @@ -17,17 +19,6 @@ namespace nbl::asset
class NBL_API2 CPolygonGeometryManipulator
{
public:
//vertex data needed for CSmoothNormalGenerator
struct SSNGVertexData
{
uint32_t index; //offset of the vertex into index buffer
uint32_t hash; //
float wage; //angle wage of the vertex
hlsl::float32_t3 position; //position of the vertex in 3D space
hlsl::float32_t3 parentTriangleFaceNormal; //
};

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

static inline void recomputeContentHashes(ICPUPolygonGeometry* geo)
{
Expand Down Expand Up @@ -243,11 +234,12 @@ class NBL_API2 CPolygonGeometryManipulator

static core::smart_refctd_ptr<ICPUPolygonGeometry> createUnweldedList(const ICPUPolygonGeometry* inGeo);

using SSNGVxCmpFunction = CSmoothNormalGenerator::VxCmpFunction;
static core::smart_refctd_ptr<ICPUPolygonGeometry> createSmoothVertexNormal(const ICPUPolygonGeometry* inbuffer, bool enableWelding = false, float epsilon = 1.525e-5f,
VxCmpFunction vxcmp = [](const CPolygonGeometryManipulator::SSNGVertexData& v0, const CPolygonGeometryManipulator::SSNGVertexData& v1, const ICPUPolygonGeometry* buffer)
SSNGVxCmpFunction vxcmp = [](const CSmoothNormalGenerator::SSNGVertexData& v0, const CSmoothNormalGenerator::SSNGVertexData& v1, const ICPUPolygonGeometry* buffer)
{
static constexpr float cosOf45Deg = 0.70710678118f;
return dot(v0.parentTriangleFaceNormal,v1.parentTriangleFaceNormal) > cosOf45Deg;
return dot(normalize(v0.weightedNormal),normalize(v1.weightedNormal)) > cosOf45Deg;
});

#if 0 // TODO: REDO
Expand Down
210 changes: 210 additions & 0 deletions include/nbl/asset/utils/CVertexHashGrid.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
#ifndef _NBL_ASSET_C_VERTEX_HASH_MAP_H_INCLUDED_
#define _NBL_ASSET_C_VERTEX_HASH_MAP_H_INCLUDED_

#include "nbl/core/declarations.h"

namespace nbl::asset
{

template <typename T>
concept HashGridVertexData = requires(T obj, T const cobj, uint32_t hash) {
{ cobj.getHash() } -> std::same_as<uint32_t>;
{ obj.setHash(hash) } -> std::same_as<void>;
{ cobj.getPosition() } -> std::same_as<hlsl::float32_t3>;
};

template <typename Fn, typename T>
concept HashGridIteratorFn = HashGridVertexData<T> && requires(Fn && fn, T const cobj)
{
// return whether hash grid should continue the iteration
{ std::invoke(std::forward<Fn>(fn), cobj) } -> std::same_as<bool>;
};

template <HashGridVertexData VertexData>
class CVertexHashGrid
{
public:

using vertex_data_t = VertexData;
using collection_t = core::vector<VertexData>;
struct BucketBounds
{
collection_t::const_iterator begin;
collection_t::const_iterator end;
};

CVertexHashGrid(size_t cellSize, uint32_t hashTableMaxSizeLog2, float vertexCountReserve) :
m_cellSize(cellSize),
m_hashTableMaxSize(1llu << hashTableMaxSizeLog2),
m_sorter(createSorter(vertexCountReserve))
{
m_vertices.reserve(vertexCountReserve);
}

//inserts vertex into hash table
void add(VertexData&& vertex)
{
vertex.setHash(hash(vertex));
m_vertices.push_back(std::move(vertex));
}

void bake()
{
auto scratchBuffer = collection_t(m_vertices.size());

auto finalSortedOutput = std::visit( [&](auto& sorter)
{
return sorter(m_vertices.data(), scratchBuffer.data(), m_vertices.size(), KeyAccessor());
}, m_sorter );

if (finalSortedOutput != m_vertices.data())
m_vertices = std::move(scratchBuffer);
}

const collection_t& vertices() const { return m_vertices; }

inline uint32_t getVertexCount() const { return m_vertices.size(); }

template <HashGridIteratorFn<VertexData> Fn>
void forEachBroadphaseNeighborCandidates(const VertexData& vertex, Fn&& fn) const
{
std::array<uint32_t, 8> neighboringCells;
const auto cellCount = getNeighboringCellHashes(neighboringCells.data(), vertex);

//iterate among all neighboring cells
for (uint8_t i = 0; i < cellCount; i++)
{
const auto& neighborCell = neighboringCells[i];
BucketBounds bounds = getBucketBoundsByHash(neighborCell);
for (; bounds.begin != bounds.end; bounds.begin++)
{
const vertex_data_t& neighborVertex = *bounds.begin;
if (&vertex != &neighborVertex)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imho the "not checking against oneself" thing could be done in the fn if we change the iterateBroadphaseCandidates to take a position and not VertexData

if (!std::invoke(std::forward<Fn>(fn), neighborVertex)) break;
}
}

};

private:
struct KeyAccessor
{
constexpr static size_t key_bit_count = 32ull;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inline is missing, its 3 keywords


template<auto bit_offset, auto radix_mask>
inline decltype(radix_mask) operator()(const VertexData& item) const
{
return static_cast<decltype(radix_mask)>(item.getHash() >> static_cast<uint32_t>(bit_offset)) & radix_mask;
}
};

static constexpr uint32_t primeNumber1 = 73856093;
static constexpr uint32_t primeNumber2 = 19349663;
static constexpr uint32_t primeNumber3 = 83492791;

using sorter_t = std::variant<
core::RadixLsbSorter<KeyAccessor::key_bit_count, uint16_t>,
core::RadixLsbSorter<KeyAccessor::key_bit_count, uint32_t>,
core::RadixLsbSorter<KeyAccessor::key_bit_count, size_t>>;
sorter_t m_sorter;

static sorter_t createSorter(size_t vertexCount)
{
if (vertexCount < (0x1ull << 16ull))
return core::RadixLsbSorter<KeyAccessor::key_bit_count, uint16_t>();
if (vertexCount < (0x1ull << 32ull))
return core::RadixLsbSorter<KeyAccessor::key_bit_count, uint32_t>();
return core::RadixLsbSorter<KeyAccessor::key_bit_count, size_t>();
}

collection_t m_vertices;
const uint32_t m_hashTableMaxSize;
const float m_cellSize;

uint32_t hash(const VertexData& vertex) const
{
const hlsl::float32_t3 position = floor(vertex.getPosition() / m_cellSize);
const auto position_uint32 = hlsl::uint32_t3(position.x, position.y, position.z);
return hash(position_uint32);
}

uint32_t hash(const hlsl::uint32_t3& position) const
{
return ((position.x * primeNumber1) ^
(position.y * primeNumber2) ^
(position.z * primeNumber3))& (m_hashTableMaxSize - 1);
}

uint8_t getNeighboringCellHashes(uint32_t* outNeighbors, const VertexData& vertex) const
{
// both 0.x and -0.x would be converted to 0 if we directly casting the position to unsigned integer. Causing the 0 to be crowded then the rest of the cells. So we use floor here to spread the vertex more uniformly.
hlsl::float32_t3 cellfloatcoord = floor(vertex.getPosition() / m_cellSize - hlsl::float32_t3(0.5));
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));
Comment on lines +141 to +142
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you might wanna summarize in comments and an ASCII diagram the logic I had to explain on discord

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i meant the justification for the 0.5, so far you only have the floor


uint8_t neighborCount = 0;

outNeighbors[neighborCount] = hash(baseCoord);
neighborCount++;

auto addUniqueNeighbor = [&neighborCount, outNeighbors](uint32_t hashval)
{
if (std::find(outNeighbors, outNeighbors + neighborCount, hashval) == outNeighbors + neighborCount)
{
outNeighbors[neighborCount] = hashval;
neighborCount++;
}
};

addUniqueNeighbor(hash(baseCoord + hlsl::uint32_t3(0, 0, 1)));
addUniqueNeighbor(hash(baseCoord + hlsl::uint32_t3(0, 1, 0)));
addUniqueNeighbor(hash(baseCoord + hlsl::uint32_t3(1, 0, 0)));
addUniqueNeighbor(hash(baseCoord + hlsl::uint32_t3(1, 1, 0)));
addUniqueNeighbor(hash(baseCoord + hlsl::uint32_t3(1, 0, 1)));
addUniqueNeighbor(hash(baseCoord + hlsl::uint32_t3(0, 1, 1)));
addUniqueNeighbor(hash(baseCoord + hlsl::uint32_t3(1, 1, 1)));

return neighborCount;
}

BucketBounds getBucketBoundsByHash(uint32_t hash) const
{
const auto skipListBound = std::visit([&](auto& sorter)
{
auto hashBound = sorter.getMostSignificantRadixBound(hash);
return std::pair<collection_t::const_iterator, collection_t::const_iterator>(m_vertices.begin() + hashBound.first, m_vertices.begin() + hashBound.second);
}, m_sorter);

auto begin = std::lower_bound(
skipListBound.first,
skipListBound.second,
hash,
[](const VertexData& vertex, uint32_t hash)
{
return vertex.getHash() < hash;
});

auto end = std::upper_bound(
skipListBound.first,
skipListBound.second,
hash,
[](uint32_t hash, const VertexData& vertex)
{
return hash < vertex.getHash();
});

const auto beginIx = begin - m_vertices.begin();
const auto endIx = end - m_vertices.begin();
//bucket missing
if (begin == end)
return { m_vertices.end(), m_vertices.end() };

//bucket missing
if (begin->hash != hash)
return { m_vertices.end(), m_vertices.end() };

return { begin, end };
}
};

}
#endif
Loading
Loading