-
Notifications
You must be signed in to change notification settings - Fork 70
Expand file tree
/
Copy pathCSmoothNormalGenerator.cpp
More file actions
127 lines (102 loc) · 4.88 KB
/
CSmoothNormalGenerator.cpp
File metadata and controls
127 lines (102 loc) · 4.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// Copyright (C) 2018-2020 - 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
#include "CSmoothNormalGenerator.h"
#include "nbl/core/declarations.h"
#include "nbl/builtin/hlsl/shapes/triangle.hlsl"
#include <algorithm>
namespace nbl
{
namespace asset
{
static bool operator<(uint32_t lhs, const CSmoothNormalGenerator::VertexData& rhs)
{
return lhs < rhs.hash;
}
static bool operator<(const CSmoothNormalGenerator::VertexData& lhs, uint32_t rhs)
{
return lhs.hash < rhs;
}
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);
}
CSmoothNormalGenerator::Result CSmoothNormalGenerator::calculateNormals(const asset::ICPUPolygonGeometry* polygon, float epsilon, VxCmpFunction vxcmp)
{
assert(polygon->getIndexingCallback()->degree() == 3);
static constexpr auto MinEpsilon = 0.00001f;
const auto patchedEpsilon = epsilon < MinEpsilon ? MinEpsilon : epsilon;
VertexHashMap vertexHashMap = setupData(polygon, patchedEpsilon);
const auto smoothPolygon = processConnectedVertices(polygon, vertexHashMap, patchedEpsilon,vxcmp);
return { vertexHashMap, smoothPolygon };
}
CSmoothNormalGenerator::VertexHashMap CSmoothNormalGenerator::setupData(const asset::ICPUPolygonGeometry* polygon, float epsilon)
{
const size_t idxCount = polygon->getPrimitiveCount() * 3;
const auto cellCount = std::max<uint32_t>(core::roundUpToPoT<uint32_t>((idxCount + 31) >> 5), 4);
VertexHashMap vertices(idxCount, std::min(16u * 1024u, cellCount), epsilon * 2.f);
for (uint32_t i = 0; i < idxCount; i += 3)
{
//calculate face normal of parent triangle
hlsl::float32_t3 v0, v1, v2;
polygon->getPositionView().decodeElement<hlsl::float32_t3>(i, v0);
polygon->getPositionView().decodeElement<hlsl::float32_t3>(i + 1, v1);
polygon->getPositionView().decodeElement<hlsl::float32_t3>(i + 2, v2);
const auto faceNormal = normalize(cross(v1 - v0, v2 - v0));
//set data for m_vertices
const auto angleWages = hlsl::shapes::util::anglesFromTriangleEdge(v2 - v1, v0 - v2, v1 - v2);
vertices.add({ i, 0, faceNormal * angleWages.x, v0});
vertices.add({ i + 1, 0, faceNormal * angleWages.y,v1});
vertices.add({ i + 2, 0, faceNormal * angleWages.z, v2});
}
vertices.bake();
return vertices;
}
core::smart_refctd_ptr<ICPUPolygonGeometry> CSmoothNormalGenerator::processConnectedVertices(const asset::ICPUPolygonGeometry* polygon, VertexHashMap& vertexHashMap, float epsilon, VxCmpFunction vxcmp)
{
auto outPolygon = core::move_and_static_cast<ICPUPolygonGeometry>(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) }
});
auto* normalPtr = reinterpret_cast<std::byte*>(outPolygon->getNormalAccessor().getPointer());
constexpr auto normalStride = sizeof(hlsl::float32_t3);
assert(outPolygon->getNormalView().composed.stride==normalStride);
for (auto& processedVertex : vertexHashMap.vertices())
{
auto normal = processedVertex.weightedNormal;
// We perform double the work (since `vxcmp` must be commutative but not required to be associative) intentionally,
// because without guaranteed associativity we cannot partition the vertices into disjoint sets (we're not reconstructing OBJ-like
// smooth groups with this), so we can't have all vertices in a set just copy their normal from a "master vertex".
// For an example of why that is good, think of a cone or cylinder and why its good to have non-associative smoothing predicate.
vertexHashMap.forEachBroadphaseNeighborCandidates(processedVertex.getPosition(), [&](const VertexHashMap::vertex_data_t& candidate)
{
if (processedVertex.index != candidate.index && compareVertexPosition(processedVertex.position, candidate.position, epsilon) &&
vxcmp(processedVertex, candidate, polygon))
{
//TODO: better mean calculation algorithm
normal += candidate.weightedNormal;
}
return true;
});
normal = normalize(normal);
memcpy(normalPtr + (normalStride * processedVertex.index), &normal, sizeof(normal));
}
CPolygonGeometryManipulator::recomputeContentHashes(outPolygon.get());
return outPolygon;
}
}
}