Line | Branch | Exec | Source |
---|---|---|---|
1 | // Copyright Contributors to the OpenVDB Project | ||
2 | // SPDX-License-Identifier: MPL-2.0 | ||
3 | |||
4 | #ifndef OPENVDB_MATH_QUANTIZED_UNIT_VEC_HAS_BEEN_INCLUDED | ||
5 | #define OPENVDB_MATH_QUANTIZED_UNIT_VEC_HAS_BEEN_INCLUDED | ||
6 | |||
7 | #include <openvdb/Platform.h> | ||
8 | #include <openvdb/version.h> | ||
9 | #include "Vec3.h" | ||
10 | |||
11 | namespace openvdb { | ||
12 | OPENVDB_USE_VERSION_NAMESPACE | ||
13 | namespace OPENVDB_VERSION_NAME { | ||
14 | namespace math { | ||
15 | |||
16 | /// @brief Unit vector occupying only 16 bits | ||
17 | /// @details Stores two quantized components. Based on the | ||
18 | /// "Higher Accuracy Quantized Normals" article from GameDev.Net LLC, 2000 | ||
19 | class OPENVDB_API QuantizedUnitVec | ||
20 | { | ||
21 | public: | ||
22 | template<typename T> static uint16_t pack(const Vec3<T>& vec); | ||
23 | static Vec3s unpack(const uint16_t data); | ||
24 | |||
25 | static void flipSignBits(uint16_t&); | ||
26 | |||
27 | private: | ||
28 | QuantizedUnitVec() {} | ||
29 | |||
30 | // bit masks | ||
31 | static const uint16_t MASK_SLOTS = 0x1FFF; // 0001111111111111 | ||
32 | static const uint16_t MASK_XSLOT = 0x1F80; // 0001111110000000 | ||
33 | static const uint16_t MASK_YSLOT = 0x007F; // 0000000001111111 | ||
34 | static const uint16_t MASK_XSIGN = 0x8000; // 1000000000000000 | ||
35 | static const uint16_t MASK_YSIGN = 0x4000; // 0100000000000000 | ||
36 | static const uint16_t MASK_ZSIGN = 0x2000; // 0010000000000000 | ||
37 | |||
38 | // normalization weights, 32 kilobytes. | ||
39 | static float sNormalizationWeights[MASK_SLOTS + 1]; | ||
40 | }; // class QuantizedUnitVec | ||
41 | |||
42 | |||
43 | //////////////////////////////////////// | ||
44 | |||
45 | |||
46 | template<typename T> | ||
47 | inline uint16_t | ||
48 |
2/2✓ Branch 0 taken 324 times.
✓ Branch 1 taken 40007 times.
|
40331 | QuantizedUnitVec::pack(const Vec3<T>& vec) |
49 | { | ||
50 | if (math::isZero(vec)) return 0; | ||
51 | |||
52 | uint16_t data = 0; | ||
53 | T x(vec[0]), y(vec[1]), z(vec[2]); | ||
54 | |||
55 | // The sign of the three components are first stored using | ||
56 | // 3-bits and can then safely be discarded. | ||
57 |
2/2✓ Branch 0 taken 20001 times.
✓ Branch 1 taken 20006 times.
|
40007 | if (x < T(0.0)) { data |= MASK_XSIGN; x = -x; } |
58 |
2/2✓ Branch 0 taken 20002 times.
✓ Branch 1 taken 20005 times.
|
40007 | if (y < T(0.0)) { data |= MASK_YSIGN; y = -y; } |
59 |
2/2✓ Branch 0 taken 20002 times.
✓ Branch 1 taken 20005 times.
|
40007 | if (z < T(0.0)) { data |= MASK_ZSIGN; z = -z; } |
60 | |||
61 | // The z component is discarded and x & y are quantized in | ||
62 | // the 0 to 126 range. | ||
63 | 40007 | T w = T(126.0) / (x + y + z); | |
64 | 40007 | uint16_t xbits = static_cast<uint16_t>((x * w)); | |
65 | 40007 | uint16_t ybits = static_cast<uint16_t>((y * w)); | |
66 | |||
67 | // The remaining 13 bits in our 16 bit word are dividied into a | ||
68 | // 6-bit x-slot and a 7-bit y-slot. Both the xbits and the ybits | ||
69 | // can still be represented using (2^7 - 1) quantization levels. | ||
70 | |||
71 | // If the xbits requre more than 6-bits, store the complement. | ||
72 | // (xbits + ybits < 127, thus if xbits > 63 => ybits <= 63) | ||
73 |
2/2✓ Branch 0 taken 5584 times.
✓ Branch 1 taken 34423 times.
|
40007 | if (xbits > 63) { |
74 | 5584 | xbits = static_cast<uint16_t>(127 - xbits); | |
75 | 5584 | ybits = static_cast<uint16_t>(127 - ybits); | |
76 | } | ||
77 | |||
78 | // Pack components into their respective slots. | ||
79 | 40007 | data = static_cast<uint16_t>(data | (xbits << 7)); | |
80 | 40007 | data = static_cast<uint16_t>(data | ybits); | |
81 | 40007 | return data; | |
82 | } | ||
83 | |||
84 | |||
85 | inline Vec3s | ||
86 | 40009 | QuantizedUnitVec::unpack(const uint16_t data) | |
87 | { | ||
88 | 40009 | const float w = sNormalizationWeights[data & MASK_SLOTS]; | |
89 | |||
90 | 40009 | uint16_t xbits = static_cast<uint16_t>((data & MASK_XSLOT) >> 7); | |
91 | 40009 | uint16_t ybits = static_cast<uint16_t>(data & MASK_YSLOT); | |
92 | |||
93 | // Check if the complement components where stored and revert. | ||
94 |
2/2✓ Branch 0 taken 5586 times.
✓ Branch 1 taken 34423 times.
|
40009 | if ((xbits + ybits) > 126) { |
95 | 5586 | xbits = static_cast<uint16_t>(127 - xbits); | |
96 | 5586 | ybits = static_cast<uint16_t>(127 - ybits); | |
97 | } | ||
98 | |||
99 |
2/2✓ Branch 0 taken 20000 times.
✓ Branch 1 taken 20009 times.
|
40009 | Vec3s vec(float(xbits) * w, float(ybits) * w, float(126 - xbits - ybits) * w); |
100 | |||
101 |
2/2✓ Branch 0 taken 20000 times.
✓ Branch 1 taken 20009 times.
|
40009 | if (data & MASK_XSIGN) vec[0] = -vec[0]; |
102 |
2/2✓ Branch 0 taken 20000 times.
✓ Branch 1 taken 20009 times.
|
40009 | if (data & MASK_YSIGN) vec[1] = -vec[1]; |
103 |
2/2✓ Branch 0 taken 20000 times.
✓ Branch 1 taken 20009 times.
|
40009 | if (data & MASK_ZSIGN) vec[2] = -vec[2]; |
104 | 40009 | return vec; | |
105 | } | ||
106 | |||
107 | |||
108 | //////////////////////////////////////// | ||
109 | |||
110 | |||
111 | inline void | ||
112 | QuantizedUnitVec::flipSignBits(uint16_t& v) | ||
113 | { | ||
114 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
2 | v = static_cast<uint16_t>((v & MASK_SLOTS) | (~v & ~MASK_SLOTS)); |
115 | } | ||
116 | |||
117 | } // namespace math | ||
118 | } // namespace OPENVDB_VERSION_NAME | ||
119 | } // namespace openvdb | ||
120 | |||
121 | #endif // OPENVDB_MATH_QUANTIZED_UNIT_VEC_HAS_BEEN_INCLUDED | ||
122 |