File: VertexHashing.h

package info (click to toggle)
darkradiant 3.9.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 41,080 kB
  • sloc: cpp: 264,743; ansic: 10,659; python: 1,852; xml: 1,650; sh: 92; makefile: 21
file content (118 lines) | stat: -rw-r--r-- 4,706 bytes parent folder | download | duplicates (3)
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
#pragma once

#include "math/Vector3.h"
#include "render/MeshVertex.h"
#include "render/RenderVertex.h"
#include "math/Hash.h"

/**
* greebo: When creating model surfaces from ASE models, the in-game loading code 
* will aim to merge vertices that are near each other (and have otherwise negligible
* differences for their other components like normal, texcoords and colour).
* 
* The in-game code is using a custom vertex hash index implementation to quickly
* look up almost-similar vertices and prevent such "duplicates" from ending up in the 
* final mesh. This is based on a set of epsilon (one for each type, in CVARs like 
* r_slopVertex, r_slopNormal, etc.) and to speed things up, a hash index is built
* for all the vertices in a single mesh.
* 
* The hash functors below aim to reproduce this behaviour without fully re-implementing
* those custom containers in the engine code, designed to be used in a std::unordered_map
* with MeshVertex used as Key type.
* 
* The MeshVertex hash functions is deliberately coarse and will produce lots of
* collisions for vertices in a certain vicinity, only provide a fast way of looking up 
* duplicates in meshes with many verts. Each "colliding" vertex will then be compared
* in-depth by the std::equal_to<> specialisation, where the epsilon-comparison is performed.
*/
namespace render
{
    // These epsilons below correspond to the CVARs in the game code
    constexpr double VertexEpsilon = 0.01; // r_slopVertex
    constexpr double NormalEpsilon = 0.02; // r_slopNormal
    constexpr double TexCoordEpsilon = 0.001; // r_slopTexCoord
}

// Coarse hash of the 3-component vector
// This is rounding the doubles to the SignificantVertexDigits defined above, 
// so any two vectors with their components only differing after the few significant digits
// will produce the same hash.
template<>
struct std::hash<Vector3>
{
    static constexpr std::size_t SignificantVertexDigits = 2;

    size_t operator()(const Vector3& v) const
    {
        return math::hashVector3(v, SignificantVertexDigits);
    }
};

// Coarse hash of the 3-component float vector
// This is rounding the doubles to the SignificantVertexDigits defined above, 
// so any two vectors with their components only differing after the few significant digits
// will produce the same hash.
template<>
struct std::hash<Vector3f>
{
    static constexpr std::size_t SignificantVertexDigits = 2;

    size_t operator()(const Vector3f& v) const
    {
        return math::hashVector3(v, SignificantVertexDigits);
    }
};

// Hash specialisation such that MeshVertex can be used as key type in std::unordered_map<>
// Only the 3D coordinates (the vertex member) will be considered for hash calculation,
// to intentionally produce hash collisions for vectors that are within a certain VertexEpsilon.
template<>
struct std::hash<MeshVertex>
{
    size_t operator()(const MeshVertex& v) const
    {
        // We just hash the vertex
        return std::hash<Vector3>()(v.vertex);
    }
};

// Assumes equality of two MeshVertices if all of (vertex, normal, texcoord, colour) 
// are equal within defined epsilons (VertexEpsilon, NormalEpsilon, TexCoordEpsilon)
template<>
struct std::equal_to<MeshVertex>
{
    bool operator()(const MeshVertex& a, const MeshVertex& b) const
    {
        return math::isNear(a.vertex, b.vertex, render::VertexEpsilon) &&
            a.normal.dot(b.normal) > (1.0 - render::NormalEpsilon) &&
            math::isNear(a.texcoord, b.texcoord, render::TexCoordEpsilon) &&
            math::isNear(a.colour, b.colour, render::VertexEpsilon);
    }
};

// Hash specialisation such that MeshVertex can be used as key type in std::unordered_map<>
// Only the 3D coordinates (the vertex member) will be considered for hash calculation,
// to intentionally produce hash collisions for vectors that are within a certain VertexEpsilon.
template<>
struct std::hash<render::RenderVertex>
{
    size_t operator()(const render::RenderVertex& v) const
    {
        // We just hash the vertex
        return std::hash<Vector3f>()(v.vertex);
    }
};

// Assumes equality of two MeshVertices if all of (vertex, normal, texcoord, colour) 
// are equal within defined epsilons (VertexEpsilon, NormalEpsilon, TexCoordEpsilon)
template<>
struct std::equal_to<render::RenderVertex>
{
    bool operator()(const render::RenderVertex& a, const render::RenderVertex& b) const
    {
        return math::isNear(a.vertex, b.vertex, render::VertexEpsilon) &&
            a.normal.dot(b.normal) > (1.0 - render::NormalEpsilon) &&
            math::isNear(a.texcoord, b.texcoord, render::TexCoordEpsilon) &&
            math::isNear(a.colour, b.colour, render::VertexEpsilon);
    }
};