| 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 |