diff --git a/examples_tests b/examples_tests index 03c3726b51..eeea1ebe20 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 03c3726b51fe1c5b5f9f53527d6f2a0d08dc64d5 +Subproject commit eeea1ebe20f661d1cd0dffc2fb7864d99c2218de diff --git a/include/nbl/asset/utils/CPolygonGeometryManipulator.h b/include/nbl/asset/utils/CPolygonGeometryManipulator.h index b9ee660309..b1182fd983 100644 --- a/include/nbl/asset/utils/CPolygonGeometryManipulator.h +++ b/include/nbl/asset/utils/CPolygonGeometryManipulator.h @@ -9,6 +9,7 @@ #include "nbl/asset/ICPUPolygonGeometry.h" #include "nbl/asset/utils/CGeometryManipulator.h" +#include "nbl/asset/utils/CVertexHashGrid.h" namespace nbl::asset { @@ -17,15 +18,31 @@ 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; // - }; + + struct SSNGVertexData + { + uint64_t index; //offset of the vertex into index buffer + uint32_t hash; + hlsl::float32_t3 weightedNormal; + // 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? + hlsl::float32_t3 position; //position of the vertex in 3D space + + hlsl::float32_t3 getPosition() const + { + return position; + } + + void setHash(uint32_t hash) + { + this->hash = hash; + } + + uint32_t getHash() const + { + return hash; + }; + + }; using VxCmpFunction = std::function; @@ -247,7 +264,7 @@ class NBL_API2 CPolygonGeometryManipulator VxCmpFunction vxcmp = [](const CPolygonGeometryManipulator::SSNGVertexData& v0, const CPolygonGeometryManipulator::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 diff --git a/include/nbl/asset/utils/CVertexHashGrid.h b/include/nbl/asset/utils/CVertexHashGrid.h new file mode 100644 index 0000000000..b978b9f576 --- /dev/null +++ b/include/nbl/asset/utils/CVertexHashGrid.h @@ -0,0 +1,212 @@ +#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 +concept HashGridVertexData = requires(T obj, T const cobj, uint32_t hash) { + { cobj.getHash() } -> std::same_as; + { obj.setHash(hash) } -> std::same_as; + { cobj.getPosition() } -> std::same_as; +}; + +template +class CVertexHashGrid +{ +public: + + using vertex_data_t = VertexData; + using collection_t = core::vector; + struct BucketBounds + { + collection_t::const_iterator begin; + collection_t::const_iterator end; + }; + + CVertexHashGrid(size_t _vertexCount, uint32_t _hashTableMaxSize, float _cellSize) : + m_sorter(createSorter(_vertexCount)), + m_hashTableMaxSize(_hashTableMaxSize), + m_cellSize(_cellSize) + { + assert((core::isPoT(m_hashTableMaxSize))); + + m_vertices.reserve(_vertexCount); + } + + //inserts vertex into hash table + void add(VertexData&& vertex) + { + vertex.setHash(hash(vertex)); + m_vertices.push_back(vertex); + } + + void validate() + { + const auto oldSize = m_vertices.size(); + m_vertices.resize(oldSize*2u); + auto finalSortedOutput = std::visit( [&](auto& sorter) { return sorter(m_vertices.data(), m_vertices.data() + oldSize, oldSize, KeyAccessor()); },m_sorter ); + + if (finalSortedOutput != m_vertices.data()) + m_vertices.erase(m_vertices.begin(), m_vertices.begin() + oldSize); + else + m_vertices.resize(oldSize); + } + + const collection_t& vertices() const { return m_vertices; } + + collection_t& vertices(){ return m_vertices; } + + inline uint32_t getVertexCount() const { return m_vertices.size(); } + + template + void iterateBroadphaseCandidates(const VertexData& vertex, Fn fn) const + { + std::array 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) + if (!fn(neighborVertex)) break; + } + } + + }; + +private: + struct KeyAccessor + { + _NBL_STATIC_INLINE_CONSTEXPR size_t key_bit_count = 32ull; + + template + inline decltype(radix_mask) operator()(const VertexData& item) const + { + return static_cast(item.getHash() >> static_cast(bit_offset)) & radix_mask; + } + }; + + static constexpr uint32_t primeNumber1 = 73856093; + static constexpr uint32_t primeNumber2 = 19349663; + static constexpr uint32_t primeNumber3 = 83492791; + + static constexpr uint32_t invalidHash = 0xFFFFFFFF; + + using sorter_t = std::variant< + core::LSBSorter, + core::LSBSorter, + core::LSBSorter>; + sorter_t m_sorter; + + static sorter_t createSorter(size_t vertexCount) + { + if (vertexCount < (0x1ull << 16ull)) + return core::LSBSorter(); + if (vertexCount < (0x1ull << 32ull)) + return core::LSBSorter(); + return core::LSBSorter(); + } + + 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); + + return ((static_cast(position.x) * primeNumber1) ^ + (static_cast(position.y) * primeNumber2) ^ + (static_cast(position.z) * primeNumber3))& (m_hashTableMaxSize - 1); + } + + 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 + { + hlsl::float32_t3 cellfloatcoord = floor(vertex.getPosition() / m_cellSize - hlsl::float32_t3(0.5)); + hlsl::uint32_t3 baseCoord = hlsl::uint32_t3(static_cast(cellfloatcoord.x), static_cast(cellfloatcoord.y), static_cast(cellfloatcoord.z)); + + 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 + { + if (hash == invalidHash) + return { m_vertices.end(), m_vertices.end() }; + + const auto skipListBound = std::visit([&](auto& sorter) + { + auto hashBound = sorter.getHashBound(hash); + return std::pair(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.hash < hash; + }); + + auto end = std::upper_bound( + skipListBound.first, + skipListBound.second, + hash, + [](uint32_t hash, const VertexData& vertex) + { + return hash < vertex.hash; + }); + + 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 \ No newline at end of file diff --git a/include/nbl/asset/utils/CVertexWelder.h b/include/nbl/asset/utils/CVertexWelder.h new file mode 100644 index 0000000000..54f407fdbf --- /dev/null +++ b/include/nbl/asset/utils/CVertexWelder.h @@ -0,0 +1,133 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_C_POLYGON_VERTEX_WELDER_H_INCLUDED_ +#define _NBL_ASSET_C_POLYGON_VERTEX_WELDER_H_INCLUDED_ + +#include "nbl/asset/utils/CPolygonGeometryManipulator.h" + +namespace nbl::asset { + +class CVertexWelder { + +public: + using WeldPredicateFn = std::function; + + template + static core::smart_refctd_ptr weldVertices(const ICPUPolygonGeometry* polygon, const AccelStructureT& as, WeldPredicateFn shouldWeldFn) { + auto outPolygon = core::move_and_static_cast(polygon->clone(0u)); + outPolygon->setIndexing(IPolygonGeometryBase::TriangleList()); + + core::vector vertexIndexToAsIndex(as.getVertexCount()); + + for (uint64_t vertexData_i = 0u; vertexData_i < as.getVertexCount(); vertexData_i++) + { + const auto& vertexData = as.vertices()[vertexData_i]; + vertexIndexToAsIndex[vertexData.index] = vertexData.index; + } + + static constexpr auto INVALID_INDEX = std::numeric_limits::max(); + core::vector remappedVertexIndexes(as.getVertexCount()); + std::fill(remappedVertexIndexes.begin(), remappedVertexIndexes.end(), INVALID_INDEX); + + uint64_t maxRemappedIndex = 0; + // iterate by index, so that we always use the smallest index when multiple vertexes can be welded together + for (uint64_t index = 0; index < as.getVertexCount(); index++) + { + const auto asIndex = vertexIndexToAsIndex[index]; + const auto& vertexData = as.vertices()[asIndex]; + auto& remappedVertexIndex = remappedVertexIndexes[index]; + as.iterateBroadphaseCandidates(vertexData, [&, polygon, index](const typename AccelStructureT::vertex_data_t& neighbor) { + const auto neighborRemappedIndex = remappedVertexIndexes[neighbor.index]; + if (shouldWeldFn(polygon, index, neighbor.index) && neighborRemappedIndex != INVALID_INDEX) { + remappedVertexIndex = neighborRemappedIndex; + return false; + } + return true; + }); + if (remappedVertexIndex != INVALID_INDEX) { + remappedVertexIndex = vertexData.index; + maxRemappedIndex = vertexData.index; + } + } + + const auto& indexView = outPolygon->getIndexView(); + if (indexView) + { + auto remappedIndexView = [&] + { + const auto bytesize = indexView.src.size; + auto indices = ICPUBuffer::create({bytesize,IBuffer::EUF_INDEX_BUFFER_BIT}); + + auto retval = indexView; + retval.src.buffer = std::move(indices); + if (retval.composed.rangeFormat == IGeometryBase::EAABBFormat::U16) + retval.composed.encodedDataRange.u16.maxVx[0] = maxRemappedIndex; + else if (retval.composed.rangeFormat == IGeometryBase::EAABBFormat::U32) + retval.composed.encodedDataRange.u32.maxVx[0] = maxRemappedIndex; + + return retval; + }(); + + + auto remappedIndexes = [&]() { + auto* indexPtr = reinterpret_cast(remappedIndexView.getPointer()); + for (uint64_t index_i = 0; index_i < polygon->getIndexCount(); index_i++) + { + hlsl::vector index; + indexView.decodeElement>(index_i, index); + IndexT remappedIndex = remappedVertexIndexes[index.x]; + indexPtr[index_i] = remappedIndex; + } + }; + + if (indexView.composed.rangeFormat == IGeometryBase::EAABBFormat::U16) { + remappedIndexes.template operator()(); + } + else if (indexView.composed.rangeFormat == IGeometryBase::EAABBFormat::U32) { + remappedIndexes.template operator()(); + } + + outPolygon->setIndexView(std::move(remappedIndexView)); + } else + { + const uint32_t indexSize = (outPolygon->getPositionView().getElementCount() - 1 < std::numeric_limits::max()) ? sizeof(uint16_t) : sizeof(uint32_t); + auto remappedIndexBuffer = ICPUBuffer::create({indexSize * outPolygon->getVertexReferenceCount(), IBuffer::EUF_INDEX_BUFFER_BIT}); + auto remappedIndexView = ICPUPolygonGeometry::SDataView{ + .composed = { + .stride = indexSize, + }, + .src = { + .offset = 0, + .size = remappedIndexBuffer->getSize(), + .buffer = std::move(remappedIndexBuffer) + } + }; + + auto fillRemappedIndex = [&](){ + auto remappedIndexBufferPtr = reinterpret_cast(remappedIndexBuffer->getPointer()); + for (uint64_t index = 0; index < outPolygon->getPositionView().getElementCount(); index++) + { + remappedIndexBufferPtr[index] = remappedVertexIndexes[index]; + } + }; + + if (indexView.composed.rangeFormat == IGeometryBase::EAABBFormat::U16) { + fillRemappedIndex.template operator()(); + } + else if (indexView.composed.rangeFormat == IGeometryBase::EAABBFormat::U32) { + fillRemappedIndex.template operator()(); + } + + outPolygon->setIndexView(std::move(remappedIndexView)); + + } + + CPolygonGeometryManipulator::recomputeContentHashes(outPolygon.get()); + return outPolygon; + } +}; + +} + +#endif diff --git a/include/nbl/builtin/hlsl/shapes/triangle.hlsl b/include/nbl/builtin/hlsl/shapes/triangle.hlsl new file mode 100644 index 0000000000..2b24a6b525 --- /dev/null +++ b/include/nbl/builtin/hlsl/shapes/triangle.hlsl @@ -0,0 +1,43 @@ +// Copyright (C) 2018-2023 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_SHAPES_TRIANGLE_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SHAPES_TRIANGLE_INCLUDED_ + +#include + +namespace nbl +{ +namespace hlsl +{ +namespace shapes +{ + +namespace util +{ + template + vector GetAngleWeight(const vector& e1, const vector& e2, const vector& e3) + { + // Calculate this triangle's weight for each of its three m_vertices + // start by calculating the lengths of its sides + const float_t a = dot(e1, e1); + const float_t asqrt = sqrt(a); + const float_t b = dot(e2, e2); + const float_t bsqrt = sqrt(b); + const float_t c = dot(e3, e3); + const float_t csqrt = sqrt(c); + + // use them to find the angle at each vertex + return vector( + acosf((b + c - a) / (2.f * bsqrt * csqrt)), + acosf((-b + c + a) / (2.f * asqrt * csqrt)), + acosf((b - c + a) / (2.f * bsqrt * asqrt))); + } +} + +} +} +} + +#endif diff --git a/include/nbl/core/algorithm/radix_sort.h b/include/nbl/core/algorithm/radix_sort.h index 749a8b8309..057598963a 100644 --- a/include/nbl/core/algorithm/radix_sort.h +++ b/include/nbl/core/algorithm/radix_sort.h @@ -38,10 +38,10 @@ constexpr int8_t find_msb(const T& a_variable) { static_assert(std::is_unsigned::value, "Variable must be unsigned"); - constexpr uint8_t number_of_bits = std::numeric_limits::digits; + constexpr int8_t number_of_bits = std::numeric_limits::digits; const std::bitset variable_bitset{a_variable}; - for (uint8_t msb = number_of_bits - 1; msb >= 0; msb--) + for (int8_t msb = number_of_bits - 1; msb >= 0; msb--) { if (variable_bitset[msb] == 1) return msb; @@ -49,12 +49,15 @@ constexpr int8_t find_msb(const T& a_variable) return -1; } + +} + template -struct RadixSorter +struct LSBSorter { _NBL_STATIC_INLINE_CONSTEXPR uint16_t histogram_bytesize = 8192u; _NBL_STATIC_INLINE_CONSTEXPR size_t histogram_size = size_t(histogram_bytesize)/sizeof(histogram_t); - _NBL_STATIC_INLINE_CONSTEXPR uint8_t radix_bits = find_msb(histogram_size); + _NBL_STATIC_INLINE_CONSTEXPR uint8_t radix_bits = impl::find_msb(histogram_size); _NBL_STATIC_INLINE_CONSTEXPR size_t last_pass = (key_bit_count-1ull)/size_t(radix_bits); _NBL_STATIC_INLINE_CONSTEXPR uint16_t radix_mask = (1u<(input,output,rangeSize,comp); } + + std::pair getHashBound(size_t key) const + { + constexpr histogram_t shift = static_cast(radix_bits * last_pass); + const auto histogramIx = (key >> shift) & radix_mask; + const auto boundBegin = histogramIx == 0 ? 0 : histogram[histogramIx - 1]; + return { boundBegin, histogram[histogramIx] }; + } + private: template inline RandomIt pass(RandomIt input, RandomIt output, const histogram_t rangeSize, const KeyAccessor& comp) @@ -71,39 +83,57 @@ struct RadixSorter std::fill_n(histogram,histogram_size,static_cast(0u)); // count constexpr histogram_t shift = static_cast(radix_bits*pass_ix); - for (histogram_t i=0u; i(input[i])]; // prefix sum - std::inclusive_scan(histogram,histogram+histogram_size,histogram); + std::inclusive_scan(histogram, histogram + histogram_size, histogram); // scatter - for (histogram_t i=rangeSize; i!=0u;) - { - i--; - output[--histogram[comp.template operator()(input[i])]] = input[i]; - } if constexpr (pass_ix != last_pass) + { + + for (histogram_t i = rangeSize; i != 0u;) + { + i--; + const auto& val = input[i]; + const auto& histogramIx = comp.template operator()(val); + output[--histogram[histogramIx]] = val; + } + return pass(output,input,rangeSize,comp); + } else + { + // need to preserve histogram value for the skip list, so we copy to temporary histogramArray and use that + std::array tmpHistogram; + std::copy(histogram, histogram + histogram_size, tmpHistogram.data()); + + for (histogram_t i = rangeSize; i != 0u;) + { + i--; + const auto& val = input[i]; + const auto& histogramIx = comp.template operator()(val); + output[--tmpHistogram[histogramIx]] = val; + } + return output; + } } alignas(sizeof(histogram_t)) histogram_t histogram[histogram_size]; }; -} - template inline RandomIt radix_sort(RandomIt input, RandomIt scratch, const size_t rangeSize, const KeyAccessor& comp) { assert(std::abs(std::distance(input,scratch))>=rangeSize); if (rangeSize(0x1ull<<16ull)) - return impl::RadixSorter()(input,scratch,static_cast(rangeSize),comp); + return LSBSorter()(input,scratch,static_cast(rangeSize),comp); if (rangeSize(0x1ull<<32ull)) - return impl::RadixSorter()(input,scratch,static_cast(rangeSize),comp); + return LSBSorter()(input,scratch,static_cast(rangeSize),comp); else - return impl::RadixSorter()(input,scratch,rangeSize,comp); + return LSBSorter()(input,scratch,rangeSize,comp); } //! Because Radix Sort needs O(2n) space and a number of passes dependant on the key length, the final sorted range can be either in `input` or `scratch` diff --git a/src/nbl/asset/utils/CGeometryCreator.cpp b/src/nbl/asset/utils/CGeometryCreator.cpp index 00bc425ef5..f36ee38125 100644 --- a/src/nbl/asset/utils/CGeometryCreator.cpp +++ b/src/nbl/asset/utils/CGeometryCreator.cpp @@ -83,7 +83,6 @@ template requires(std::is_same_v || std::is_same_v) static ICPUPolygonGeometry::SDataView createIndexView(size_t indexCount, size_t maxIndex) { - const auto bytesize = sizeof(IndexT) * indexCount; auto indices = ICPUBuffer::create({bytesize,IBuffer::EUF_INDEX_BUFFER_BIT}); diff --git a/src/nbl/asset/utils/CPolygonGeometryManipulator.cpp b/src/nbl/asset/utils/CPolygonGeometryManipulator.cpp index 03b07fc22f..859d1224a3 100644 --- a/src/nbl/asset/utils/CPolygonGeometryManipulator.cpp +++ b/src/nbl/asset/utils/CPolygonGeometryManipulator.cpp @@ -9,6 +9,7 @@ #include #include "nbl/asset/utils/CPolygonGeometryManipulator.h" +#include "nbl/asset/utils/CVertexWelder.h" #include "nbl/asset/utils/CSmoothNormalGenerator.h" @@ -131,6 +132,86 @@ core::smart_refctd_ptr CPolygonGeometryManipulator::createU return outGeometry; } + +namespace +{ + bool isAttributeValEqual(const ICPUPolygonGeometry::SDataView& view, uint64_t index1, uint64_t index2, float epsilon) + { + if (!view) return true; + const auto channelCount = getFormatChannelCount(view.composed.format); + switch (view.composed.rangeFormat) + { + case IGeometryBase::EAABBFormat::U64: + case IGeometryBase::EAABBFormat::U32: + { + hlsl::uint64_t4 val1, val2; + view.decodeElement(index1, val1); + view.decodeElement(index2, val2); + for (auto channel_i = 0u; channel_i < channelCount; channel_i++) + if (val1[channel_i] != val2[channel_i]) return false; + break; + } + case IGeometryBase::EAABBFormat::S64: + case IGeometryBase::EAABBFormat::S32: + { + hlsl::int64_t4 val1, val2; + view.decodeElement(index1, val1); + view.decodeElement(index2, val2); + for (auto channel_i = 0u; channel_i < channelCount; channel_i++) + if (val1[channel_i] != val2[channel_i]) return false; + break; + } + default: + { + hlsl::float64_t4 val1, val2; + view.decodeElement(index1, val1); + view.decodeElement(index2, val2); + for (auto channel_i = 0u; channel_i < channelCount; channel_i++) + { + const auto diff = abs(val1[channel_i] - val2[channel_i]); + if (diff > epsilon) return false; + } + break; + } + } + return true; + } + + bool isAttributeDirEqual(const ICPUPolygonGeometry::SDataView& view, uint64_t index1, uint64_t index2, float epsilon) +{ + if (!view) return true; + const auto channelCount = getFormatChannelCount(view.composed.format); + switch (view.composed.rangeFormat) + { + case IGeometryBase::EAABBFormat::U64: + case IGeometryBase::EAABBFormat::U32: + { + hlsl::uint64_t4 val1, val2; + view.decodeElement(index1, val1); + view.decodeElement(index2, val2); + return (1.0 - hlsl::dot(val1, val2)) < epsilon; + } + case IGeometryBase::EAABBFormat::S64: + case IGeometryBase::EAABBFormat::S32: + { + hlsl::int64_t4 val1, val2; + view.decodeElement(index1, val1); + view.decodeElement(index2, val2); + return (1.0 - hlsl::dot(val1, val2)) < epsilon; + } + default: + { + hlsl::float64_t4 val1, val2; + view.decodeElement(index1, val1); + view.decodeElement(index2, val2); + return (1.0 - hlsl::dot(val1, val2)) < epsilon; + } + } + return true; +} +} + + core::smart_refctd_ptr CPolygonGeometryManipulator::createSmoothVertexNormal(const ICPUPolygonGeometry* inPolygon, bool enableWelding, float epsilon, VxCmpFunction vxcmp) { if (inPolygon == nullptr) @@ -146,8 +227,31 @@ core::smart_refctd_ptr CPolygonGeometryManipulator::createS return nullptr; } - return CSmoothNormalGenerator::calculateNormals(inPolygon, enableWelding, epsilon, vxcmp); + auto canJoinVertices = [epsilon](const ICPUPolygonGeometry* polygon, uint32_t index1, uint32_t index2)-> bool +{ + if (!isAttributeValEqual(polygon->getPositionView(), index1, index2, epsilon)) + return false; + if (!isAttributeDirEqual(polygon->getNormalView(), index1, index2, epsilon)) + return false; + for (const auto& jointWeightView : polygon->getJointWeightViews()) + { + if (!isAttributeValEqual(jointWeightView.indices, index1, index2, epsilon)) return false; + if (!isAttributeValEqual(jointWeightView.weights, index1, index2, epsilon)) return false; + } + for (const auto& auxAttributeView : polygon->getAuxAttributeViews()) + if (!isAttributeValEqual(auxAttributeView, index1, index2, epsilon)) return false; + + return true; + }; + + auto result = CSmoothNormalGenerator::calculateNormals(inPolygon, epsilon, vxcmp); + if (enableWelding) + { + return CVertexWelder::weldVertices(result.geom.get(), result.vertexHashGrid, canJoinVertices); + } + return result.geom; } + } // end namespace nbl::asset diff --git a/src/nbl/asset/utils/CSmoothNormalGenerator.cpp b/src/nbl/asset/utils/CSmoothNormalGenerator.cpp index 73ac79b5ec..a6067adddd 100644 --- a/src/nbl/asset/utils/CSmoothNormalGenerator.cpp +++ b/src/nbl/asset/utils/CSmoothNormalGenerator.cpp @@ -5,9 +5,9 @@ #include "CSmoothNormalGenerator.h" #include "nbl/core/declarations.h" +#include "nbl/builtin/hlsl/shapes/triangle.hlsl" #include -#include namespace nbl { @@ -23,190 +23,28 @@ static bool operator<(const CPolygonGeometryManipulator::SSNGVertexData& lhs, ui return lhs.hash < rhs; } -static bool isAttributeEqual(const ICPUPolygonGeometry::SDataView& view, uint32_t index1, uint32_t index2, float epsilon) -{ - if (!view) return true; - const auto channelCount = getFormatChannelCount(view.composed.format); - switch (view.composed.rangeFormat) - { - case IGeometryBase::EAABBFormat::U64: - case IGeometryBase::EAABBFormat::U32: - { - hlsl::uint64_t4 val1, val2; - view.decodeElement(index1, val1); - view.decodeElement(index2, val2); - for (auto channel_i = 0u; channel_i < channelCount; channel_i++) - if (val1[channel_i] != val2[channel_i]) return false; - break; - } - case IGeometryBase::EAABBFormat::S64: - case IGeometryBase::EAABBFormat::S32: - { - hlsl::int64_t4 val1, val2; - view.decodeElement(index1, val1); - view.decodeElement(index2, val2); - for (auto channel_i = 0u; channel_i < channelCount; channel_i++) - if (val1[channel_i] != val2[channel_i]) return false; - break; - } - default: - { - hlsl::float64_t4 val1, val2; - view.decodeElement(index1, val1); - view.decodeElement(index2, val2); - for (auto channel_i = 0u; channel_i < channelCount; channel_i++) - { - const auto diff = abs(val1[channel_i] - val2[channel_i]); - if (diff > epsilon) return false; - } - break; - } - } - return true; -} - static bool compareVertexPosition(const hlsl::float32_t3& a, const hlsl::float32_t3& b, float epsilon) { const hlsl::float32_t3 difference = abs(b - a); return (difference.x <= epsilon && difference.y <= epsilon && difference.z <= epsilon); } -static hlsl::float32_t3 getAngleWeight( - const hlsl::float32_t3& v1, - const hlsl::float32_t3& v2, - const hlsl::float32_t3& v3) -{ - auto distancesquared = [](const hlsl::float32_t3& v1, const hlsl::float32_t3& v2) - { - const auto diff = v1 - v2; - return hlsl::dot(diff, diff); - }; - // Calculate this triangle's weight for each of its three m_vertices - // start by calculating the lengths of its sides - const float a = distancesquared(v2, v3); - const float asqrt = sqrt(a); - const float b = distancesquared(v1,v3); - const float bsqrt = sqrt(b); - const float c = distancesquared(v1,v2); - const float csqrt = sqrt(c); - - // use them to find the angle at each vertex - return hlsl::float32_t3( - acosf((b + c - a) / (2.f * bsqrt * csqrt)), - acosf((-b + c + a) / (2.f * asqrt * csqrt)), - acosf((b - c + a) / (2.f * bsqrt * asqrt))); -} - -core::smart_refctd_ptr CSmoothNormalGenerator::calculateNormals(const asset::ICPUPolygonGeometry* polygon, bool enableWelding, float epsilon, CPolygonGeometryManipulator::VxCmpFunction vxcmp) -{ - VertexHashMap vertexArray = setupData(polygon, epsilon); - const auto smoothPolygon = processConnectedVertices(polygon, vertexArray, epsilon,vxcmp); - - if (enableWelding) - { - return weldVertices(smoothPolygon.get(), vertexArray, epsilon); - } - return smoothPolygon; -} - -CSmoothNormalGenerator::VertexHashMap::VertexHashMap(size_t _vertexCount, uint32_t _hashTableMaxSize, float _cellSize) - :m_hashTableMaxSize(_hashTableMaxSize), - m_cellSize(_cellSize) -{ - assert((core::isPoT(m_hashTableMaxSize))); - - m_vertices.reserve(_vertexCount); - m_buckets.reserve(_hashTableMaxSize + 1); -} - -uint32_t CSmoothNormalGenerator::VertexHashMap::hash(const CPolygonGeometryManipulator::SSNGVertexData & vertex) const -{ - const hlsl::float32_t3 position = vertex.position / m_cellSize; - - return ((static_cast(position.x) * primeNumber1) ^ - (static_cast(position.y) * primeNumber2) ^ - (static_cast(position.z) * primeNumber3))& (m_hashTableMaxSize - 1); -} - -uint32_t CSmoothNormalGenerator::VertexHashMap::hash(const hlsl::uint32_t3& position) const -{ - return ((position.x * primeNumber1) ^ - (position.y * primeNumber2) ^ - (position.z * primeNumber3))& (m_hashTableMaxSize - 1); -} - -void CSmoothNormalGenerator::VertexHashMap::add(CPolygonGeometryManipulator::SSNGVertexData && vertex) -{ - vertex.hash = hash(vertex); - m_vertices.push_back(vertex); -} - -CSmoothNormalGenerator::VertexHashMap::BucketBounds CSmoothNormalGenerator::VertexHashMap::getBucketBoundsByHash(uint32_t hash) +CSmoothNormalGenerator::Result CSmoothNormalGenerator::calculateNormals(const asset::ICPUPolygonGeometry* polygon, float epsilon, CPolygonGeometryManipulator::VxCmpFunction vxcmp) { - if (hash == invalidHash) - return { m_vertices.end(), m_vertices.end() }; - - core::vector::iterator begin = std::lower_bound(m_vertices.begin(), m_vertices.end(), hash); - core::vector::iterator end = std::upper_bound(m_vertices.begin(), m_vertices.end(), hash); - - //bucket missing - if (begin == m_vertices.end()) - return { m_vertices.end(), m_vertices.end() }; - - //bucket missing - if (begin->hash != hash) - return { m_vertices.end(), m_vertices.end() }; - - return { begin, end }; + VertexHashMap vertexHashMap = setupData(polygon, epsilon); + const auto smoothPolygon = processConnectedVertices(polygon, vertexHashMap, epsilon,vxcmp); + return { vertexHashMap, smoothPolygon }; } -struct KeyAccessor -{ - _NBL_STATIC_INLINE_CONSTEXPR size_t key_bit_count = 32ull; - - template - inline decltype(radix_mask) operator()(const CPolygonGeometryManipulator::SSNGVertexData& item) const - { - return static_cast(item.hash>>static_cast(bit_offset))&radix_mask; - } -}; -void CSmoothNormalGenerator::VertexHashMap::validate() -{ - const auto oldSize = m_vertices.size(); - m_vertices.resize(oldSize*2u); - // TODO: maybe use counting sort (or big radix) and use the histogram directly for the m_buckets - auto finalSortedOutput = core::radix_sort(m_vertices.data(),m_vertices.data()+oldSize,oldSize,KeyAccessor()); - // TODO: optimize out the erase - if (finalSortedOutput!=m_vertices.data()) - m_vertices.erase(m_vertices.begin(),m_vertices.begin()+oldSize); - else - m_vertices.erase(m_vertices.begin()+oldSize,m_vertices.end()); - - // TODO: are `m_buckets` even begin USED!? - uint16_t prevHash = m_vertices[0].hash; - core::vector::iterator prevBegin = m_vertices.begin(); - m_buckets.push_back(prevBegin); - - while (true) - { - core::vector::iterator next = std::upper_bound(prevBegin, m_vertices.end(), prevHash); - m_buckets.push_back(next); - - if (next == m_vertices.end()) - break; - - prevBegin = next; - prevHash = next->hash; - } -} CSmoothNormalGenerator::VertexHashMap CSmoothNormalGenerator::setupData(const asset::ICPUPolygonGeometry* polygon, float epsilon) { const size_t idxCount = polygon->getPrimitiveCount() * 3; - VertexHashMap vertices(idxCount, std::min(16u * 1024u, core::roundUpToPoT(idxCount * 1.0f / 32.0f)), epsilon == 0.0f ? 0.00001f : epsilon * 2.f); + const auto cellCount = std::max(core::roundUpToPoT((idxCount + 31) >> 5), 4); + VertexHashMap vertices(idxCount, std::min(16u * 1024u, cellCount), epsilon == 0.0f ? 0.00001f : epsilon * 2.f); - for (uint32_t i = 0; i < idxCount; i += 3) + for (uint64_t i = 0; i < idxCount; i += 3) { //calculate face normal of parent triangle hlsl::float32_t3 v1, v2, v3; @@ -217,11 +55,11 @@ CSmoothNormalGenerator::VertexHashMap CSmoothNormalGenerator::setupData(const as const auto faceNormal = normalize(cross(v2 - v1, v3 - v1)); //set data for m_vertices - const auto angleWages = getAngleWeight(v1, v2, v3); + const auto angleWages = hlsl::shapes::util::GetAngleWeight(v2 - v3, v1 - v3, v1 - v2); - vertices.add({ i, 0, angleWages.x, v1, faceNormal}); - vertices.add({ i + 1, 0, angleWages.y, v2, faceNormal}); - vertices.add({ i + 2, 0, angleWages.z, v3, faceNormal}); + vertices.add({ i, 0, faceNormal * angleWages.x, v1}); + vertices.add({ i + 1, 0, faceNormal * angleWages.y,v2}); + vertices.add({ i + 2, 0, faceNormal * angleWages.z, v3}); } vertices.validate(); @@ -231,55 +69,46 @@ CSmoothNormalGenerator::VertexHashMap CSmoothNormalGenerator::setupData(const as core::smart_refctd_ptr CSmoothNormalGenerator::processConnectedVertices(const asset::ICPUPolygonGeometry* polygon, VertexHashMap& vertexHashMap, float epsilon, CPolygonGeometryManipulator::VxCmpFunction vxcmp) { - auto outPolygon = core::move_and_static_cast(polygon->clone(0u)); - static constexpr auto NormalFormat = EF_R32G32B32_SFLOAT; - const auto normalFormatBytesize = asset::getTexelOrBlockBytesize(NormalFormat); - auto normalBuf = ICPUBuffer::create({ normalFormatBytesize * outPolygon->getPositionView().getElementCount()}); - auto normalView = polygon->getNormalView(); + auto outPolygon = core::move_and_static_cast(polygon->clone(0u)); + static constexpr auto NormalFormat = EF_R32G32B32_SFLOAT; + const auto normalFormatBytesize = asset::getTexelOrBlockBytesize(NormalFormat); + auto normalBuf = ICPUBuffer::create({ normalFormatBytesize * outPolygon->getPositionView().getElementCount()}); + auto normalView = polygon->getNormalView(); - hlsl::shapes::AABB<4,hlsl::float32_t> aabb; - aabb.maxVx = hlsl::float32_t4(1, 1, 1, 0.f); - aabb.minVx = -aabb.maxVx; - outPolygon->setNormalView({ - .composed = { - .encodedDataRange = {.f32 = aabb}, - .stride = sizeof(hlsl::float32_t3), - .format = NormalFormat, - .rangeFormat = IGeometryBase::EAABBFormat::F32 - }, - .src = { .offset = 0, .size = normalBuf->getSize(), .buffer = std::move(normalBuf) }, - }); + hlsl::shapes::AABB<4,hlsl::float32_t> aabb; + aabb.maxVx = hlsl::float32_t4(1, 1, 1, 0.f); + aabb.minVx = -aabb.maxVx; + outPolygon->setNormalView({ + .composed = { + .encodedDataRange = {.f32 = aabb}, + .stride = sizeof(hlsl::float32_t3), + .format = NormalFormat, + .rangeFormat = IGeometryBase::EAABBFormat::F32 + }, + .src = { .offset = 0, .size = normalBuf->getSize(), .buffer = std::move(normalBuf) }, + }); auto* normalPtr = reinterpret_cast(outPolygon->getNormalPtr()); auto normalStride = outPolygon->getNormalView().composed.stride; - for (uint32_t cell = 0; cell < vertexHashMap.getBucketCount() - 1; cell++) - { - VertexHashMap::BucketBounds processedBucket = vertexHashMap.getBucketBoundsById(cell); - for (core::vector::iterator processedVertex = processedBucket.begin; processedVertex != processedBucket.end; processedVertex++) - { - std::array neighboringCells = vertexHashMap.getNeighboringCellHashes(*processedVertex); - hlsl::float32_t3 normal = processedVertex->parentTriangleFaceNormal * processedVertex->wage; + for (auto& processedVertex : vertexHashMap.vertices()) + { + auto normal = processedVertex.weightedNormal; - //iterate among all neighboring cells - for (int i = 0; i < 8; i++) + vertexHashMap.iterateBroadphaseCandidates(processedVertex, [&](const VertexHashMap::vertex_data_t& candidate) { - VertexHashMap::BucketBounds bounds = vertexHashMap.getBucketBoundsByHash(neighboringCells[i]); - for (; bounds.begin != bounds.end; bounds.begin++) + if (compareVertexPosition(processedVertex.position, candidate.position, epsilon) && + vxcmp(processedVertex, candidate, polygon)) { - if (processedVertex != bounds.begin) - if (compareVertexPosition(processedVertex->position, bounds.begin->position, epsilon) && - vxcmp(*processedVertex, *bounds.begin, polygon)) - { - //TODO: better mean calculation algorithm - normal += bounds.begin->parentTriangleFaceNormal * bounds.begin->wage; - } + //TODO: better mean calculation algorithm + normal += candidate.weightedNormal; } - } - normal = normalize(normal); - memcpy(normalPtr + (normalStride * processedVertex->index), &normal, sizeof(normal)); - } + return true; + }); + + normal = normalize(normal); + memcpy(normalPtr + (normalStride * processedVertex.index), &normal, sizeof(normal)); } CPolygonGeometryManipulator::recomputeContentHashes(outPolygon.get()); @@ -287,258 +116,5 @@ core::smart_refctd_ptr CSmoothNormalGenerator::processConne return outPolygon; } -std::array CSmoothNormalGenerator::VertexHashMap::getNeighboringCellHashes(const CPolygonGeometryManipulator::SSNGVertexData & vertex) -{ - std::array neighbourhood; - - hlsl::float32_t3 cellFloatCoord = vertex.position / m_cellSize - hlsl::float32_t3(0.5f); - hlsl::uint32_t3 neighbor = hlsl::uint32_t3(static_cast(cellFloatCoord.x), static_cast(cellFloatCoord.y), static_cast(cellFloatCoord.z)); - - //left bottom near - neighbourhood[0] = hash(neighbor); - - //right bottom near - neighbor = neighbor + hlsl::uint32_t3(1, 0, 0); - neighbourhood[1] = hash(neighbor); - - //right bottom far - neighbor = neighbor + hlsl::uint32_t3(0, 0, 1); - neighbourhood[2] = hash(neighbor); - - //left bottom far - neighbor = neighbor - hlsl::uint32_t3(1, 0, 0); - neighbourhood[3] = hash(neighbor); - - //left top far - neighbor = neighbor + hlsl::uint32_t3(0, 1, 0); - neighbourhood[4] = hash(neighbor); - - //right top far - neighbor = neighbor + hlsl::uint32_t3(1, 0, 0); - neighbourhood[5] = hash(neighbor); - - //righ top near - neighbor = neighbor - hlsl::uint32_t3(0, 0, 1); - neighbourhood[6] = hash(neighbor); - - //left top near - neighbor = neighbor - hlsl::uint32_t3(1, 0, 0); - neighbourhood[7] = hash(neighbor); - - //erase duplicated hashes - for (int i = 0; i < 8; i++) - { - uint32_t currHash = neighbourhood[i]; - for (int j = i + 1; j < 8; j++) - { - if (neighbourhood[j] == currHash) - neighbourhood[j] = invalidHash; - } - } - return neighbourhood; -} - -core::smart_refctd_ptr CSmoothNormalGenerator::weldVertices(const ICPUPolygonGeometry* polygon, VertexHashMap& vertices, float epsilon) -{ - struct Group - { - uint32_t vertex_reference_index; // index to referenced vertex in the original polygon - }; - core::vector groups; - groups.reserve(vertices.getVertexCount()); - - core::vector> groupIndexes(vertices.getVertexCount()); - - auto canJoinVertices = [&](uint32_t index1, uint32_t index2)-> bool - { - if (!isAttributeEqual(polygon->getPositionView(), index1, index2, epsilon)) - return false; - if (!isAttributeEqual(polygon->getNormalView(), index1, index2, epsilon)) - return false; - for (const auto& jointWeightView : polygon->getJointWeightViews()) - { - if (!isAttributeEqual(jointWeightView.indices, index1, index2, epsilon)) return false; - if (!isAttributeEqual(jointWeightView.weights, index1, index2, epsilon)) return false; - } - for (const auto& auxAttributeView : polygon->getAuxAttributeViews()) - if (!isAttributeEqual(auxAttributeView, index1, index2, epsilon)) return false; - - return true; - }; - - for (uint32_t cell = 0; cell < vertices.getBucketCount() - 1; cell++) - { - VertexHashMap::BucketBounds processedBucket = vertices.getBucketBoundsById(cell); - - for (core::vector::iterator processedVertex = processedBucket.begin; processedVertex != processedBucket.end; processedVertex++) - { - std::array neighboringCells = vertices.getNeighboringCellHashes(*processedVertex); - hlsl::float32_t3 normal = processedVertex->parentTriangleFaceNormal * processedVertex->wage; - - auto& groupIndex = groupIndexes[processedVertex->index]; - - //iterate among all neighboring cells - for (int i = 0; i < 8; i++) - { - VertexHashMap::BucketBounds bounds = vertices.getBucketBoundsByHash(neighboringCells[i]); - for (auto neighbourVertex_it = bounds.begin; neighbourVertex_it != bounds.end; neighbourVertex_it++) - { - const auto neighbourGroupIndex = groupIndexes[neighbourVertex_it->index]; - - hlsl::float32_t3 normal1, normal2; - polygon->getNormalView().decodeElement(processedVertex->index, normal1); - polygon->getNormalView().decodeElement(neighbourVertex_it->index, normal2); - - hlsl::float32_t3 position1, position2; - polygon->getPositionView().decodeElement(processedVertex->index, position1); - polygon->getPositionView().decodeElement(neighbourVertex_it->index, position2); - - // find the first group that this vertex can join - if (processedVertex != neighbourVertex_it && neighbourGroupIndex && canJoinVertices(processedVertex->index, neighbourVertex_it->index)) - { - groupIndex = neighbourGroupIndex; - break; - } - } - } - if (!groupIndex) - { - // create new group if no group nearby that is compatible with this vertex - groupIndex = groups.size(); - groups.push_back({ processedVertex->index}); - } - } - } - - auto outPolygon = core::move_and_static_cast(polygon->clone(0u)); - outPolygon->setIndexing(IPolygonGeometryBase::TriangleList()); - - const uint32_t indexSize = (groups.size() < std::numeric_limits::max()) ? sizeof(uint16_t) : sizeof(uint32_t); - auto indexBuffer = ICPUBuffer::create({indexSize * groupIndexes.size(), IBuffer::EUF_INDEX_BUFFER_BIT}); - auto indexBufferPtr = reinterpret_cast(indexBuffer->getPointer()); - auto indexView = ICPUPolygonGeometry::SDataView{ - .composed = { - .stride = indexSize, - }, - .src = { - .offset = 0, - .size = indexBuffer->getSize(), - .buffer = std::move(indexBuffer) - } - }; - if (indexSize == 2) - { - indexView.composed.encodedDataRange.u16.minVx[0] = 0; - indexView.composed.encodedDataRange.u16.maxVx[0] = groups.size() - 1; - indexView.composed.format = EF_R16_UINT; - indexView.composed.rangeFormat = IGeometryBase::EAABBFormat::U16; - } else if (indexSize == 4) - { - indexView.composed.encodedDataRange.u32.minVx[0] = 0; - indexView.composed.encodedDataRange.u32.maxVx[0] = groups.size() - 1; - indexView.composed.format = EF_R32_UINT; - indexView.composed.rangeFormat = IGeometryBase::EAABBFormat::U32; - } - - for (auto index_i = 0u; index_i < groupIndexes.size(); index_i++) - { - if (indexSize == 2) - { - uint16_t index = *groupIndexes[index_i]; - memcpy(indexBufferPtr + indexSize * index_i, &index, sizeof(index)); - } else if (indexSize == 4) - { - uint32_t index = *groupIndexes[index_i]; - memcpy(indexBufferPtr + indexSize * index_i, &index, sizeof(index)); - } - } - outPolygon->setIndexView(std::move(indexView)); - - - using position_t = hlsl::float32_t3; - constexpr auto PositionAttrSize = sizeof(position_t); - auto positionBuffer = ICPUBuffer::create({ PositionAttrSize * groups.size(), IBuffer::EUF_NONE }); - auto outPositions = reinterpret_cast(positionBuffer->getPointer()); - const auto inPositions = reinterpret_cast(polygon->getPositionView().getPointer()); - outPolygon->setPositionView({ - .composed = polygon->getPositionView().composed, - .src = {.offset = 0, .size = positionBuffer->getSize(), .buffer = std::move(positionBuffer)} - }); - - using normal_t = hlsl::float32_t3; - constexpr auto NormalAttrSize = sizeof(normal_t); - auto normalBuffer = ICPUBuffer::create({ NormalAttrSize * groups.size(), IBuffer::EUF_NONE }); - auto outNormals = reinterpret_cast(normalBuffer->getPointer()); - const auto inNormals = reinterpret_cast(polygon->getNormalView().getPointer()); - outPolygon->setNormalView({ - .composed = polygon->getNormalView().composed, - .src = {.offset = 0, .size = normalBuffer->getSize(), .buffer = std::move(normalBuffer)} - }); - - auto createOutView = [&](const ICPUPolygonGeometry::SDataView& view) - { - auto buffer = ICPUBuffer::create({ view.composed.stride * groups.size(), view.src.buffer->getUsageFlags() }); - return ICPUPolygonGeometry::SDataView{ - .composed = view.composed, - .src = {.offset = 0, .size = buffer->getSize(), .buffer = std::move(buffer)} - }; - }; - - const auto& inJointWeightViews = polygon->getJointWeightViews(); - auto* outJointWeightViews = outPolygon->getJointWeightViews(); - outJointWeightViews->resize(inJointWeightViews.size()); - for (auto jointWeightView_i = 0u; jointWeightView_i < inJointWeightViews.size(); jointWeightView_i++) - { - const auto& inJointWeightView = inJointWeightViews[jointWeightView_i]; - outJointWeightViews->operator[](jointWeightView_i).indices = createOutView(inJointWeightView.indices); - outJointWeightViews->operator[](jointWeightView_i).weights = createOutView(inJointWeightView.weights); - } - - const auto& inAuxAttributeViews = polygon->getAuxAttributeViews(); - auto* outAuxAttributeViews = outPolygon->getAuxAttributeViews(); - outAuxAttributeViews->resize(inAuxAttributeViews.size()); - for (auto auxAttributeView_i = 0u; auxAttributeView_i < inAuxAttributeViews.size(); auxAttributeView_i++) - { - const auto& inAuxAttributeView = inAuxAttributeViews[auxAttributeView_i]; - outAuxAttributeViews->operator[](auxAttributeView_i) = createOutView(inAuxAttributeView); - } - - for (auto group_i = 0u; group_i < groups.size(); group_i++) - { - const auto srcIndex = groups[group_i].vertex_reference_index; - outPositions[group_i] = inPositions[srcIndex]; - outNormals[group_i] = inPositions[srcIndex]; - - for (uint64_t jointView_i = 0u; jointView_i < polygon->getJointWeightViews().size(); jointView_i++) - { - auto& inView = polygon->getJointWeightViews()[jointView_i]; - auto& outView = outPolygon->getJointWeightViews()->operator[](jointView_i); - - const std::byte* const inJointIndices = reinterpret_cast(inView.indices.getPointer()); - const auto jointIndexSize = inView.indices.composed.stride; - std::byte* const outJointIndices = reinterpret_cast(outView.indices.getPointer()); - memcpy(outJointIndices + group_i * jointIndexSize, inJointIndices + srcIndex * jointIndexSize, jointIndexSize); - - const std::byte* const inWeights = reinterpret_cast(inView.weights.getPointer()); - const auto jointWeightSize = inView.weights.composed.stride; - std::byte* const outWeights = reinterpret_cast(outView.weights.getPointer()); - memcpy(outWeights + group_i * jointWeightSize, inWeights + srcIndex * jointWeightSize, jointWeightSize); - } - - for (auto auxView_i = 0u; auxView_i < polygon->getAuxAttributeViews().size(); auxView_i++) - { - auto& inView = polygon->getAuxAttributeViews()[auxView_i]; - auto& outView = outPolygon->getAuxAttributeViews()->operator[](auxView_i); - const auto attrSize = inView.composed.stride; - const std::byte* const inAuxs = reinterpret_cast(inView.getPointer()); - std::byte* const outAuxs = reinterpret_cast(outView.getPointer()); - memcpy(outAuxs + group_i * attrSize, inAuxs + srcIndex * attrSize, attrSize); - } - } - - CPolygonGeometryManipulator::recomputeContentHashes(outPolygon.get()); - return outPolygon; - -} } } \ No newline at end of file diff --git a/src/nbl/asset/utils/CSmoothNormalGenerator.h b/src/nbl/asset/utils/CSmoothNormalGenerator.h index c7d648d2d7..90c72e45ee 100644 --- a/src/nbl/asset/utils/CSmoothNormalGenerator.h +++ b/src/nbl/asset/utils/CSmoothNormalGenerator.h @@ -16,57 +16,19 @@ class CSmoothNormalGenerator CSmoothNormalGenerator() = delete; ~CSmoothNormalGenerator() = delete; - static core::smart_refctd_ptr calculateNormals(const ICPUPolygonGeometry* polygon, bool enableWelding, float epsilon, CPolygonGeometryManipulator::VxCmpFunction function); + using VertexHashMap = CVertexHashGrid; - private: - class VertexHashMap - { - public: - struct BucketBounds - { - core::vector::iterator begin; - core::vector::iterator end; - }; - - public: - VertexHashMap(size_t _vertexCount, uint32_t _hashTableMaxSize, float _cellSize); - - //inserts vertex into hash table - void add(CPolygonGeometryManipulator::SSNGVertexData&& vertex); - - //sorts hashtable and sets iterators at beginnings of bucktes - void validate(); - - inline uint32_t getVertexCount() const { return m_vertices.size(); } - - // - std::array getNeighboringCellHashes(const CPolygonGeometryManipulator::SSNGVertexData& vertex); - - inline uint32_t getBucketCount() { return m_buckets.size(); } - inline BucketBounds getBucketBoundsById(uint32_t index) const { return { m_buckets[index], m_buckets[index + 1] }; } - BucketBounds getBucketBoundsByHash(uint32_t hash); - - private: - static constexpr uint32_t invalidHash = 0xFFFFFFFF; - static constexpr uint32_t primeNumber1 = 73856093; - static constexpr uint32_t primeNumber2 = 19349663; - static constexpr uint32_t primeNumber3 = 83492791; - - //holds iterators pointing to beginning of each bucket, last iterator points to m_vertices.end() - core::vector::iterator> m_buckets; - core::vector m_vertices; - const uint32_t m_hashTableMaxSize; - const float m_cellSize; - - uint32_t hash(const CPolygonGeometryManipulator::SSNGVertexData& vertex) const; - uint32_t hash(const hlsl::uint32_t3& position) const; - - }; + struct Result + { + VertexHashMap vertexHashGrid; + core::smart_refctd_ptr geom; + }; + static Result calculateNormals(const ICPUPolygonGeometry* polygon, float epsilon, CPolygonGeometryManipulator::VxCmpFunction function); private: + static VertexHashMap setupData(const ICPUPolygonGeometry* polygon, float epsilon); static core::smart_refctd_ptr processConnectedVertices(const ICPUPolygonGeometry* polygon, VertexHashMap& vertices, float epsilon, CPolygonGeometryManipulator::VxCmpFunction vxcmp); - static core::smart_refctd_ptr weldVertices(const ICPUPolygonGeometry* polygon, VertexHashMap& vertices, float epsilon); }; } diff --git a/src/nbl/builtin/CMakeLists.txt b/src/nbl/builtin/CMakeLists.txt index 3b9fe1c39a..96fb60d535 100644 --- a/src/nbl/builtin/CMakeLists.txt +++ b/src/nbl/builtin/CMakeLists.txt @@ -317,6 +317,7 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/circle.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/ellipse.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/line.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/beziers.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/triangle.hlsl") # LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/ndarray_addressing.hlsl") #