/* ========================================================================== * Copyright (c) 2022 SuperTuxKart-Team * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the * following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * ========================================================================== */ #ifndef HEADER_MINI_GLM_HPP #define HEADER_MINI_GLM_HPP #include "LinearMath/btQuaternion.h" #include "LinearMath/btTransform.h" #include "LinearMath/btVector3.h" #include #include #include #include #include #include #include #include "irrMath.h" using namespace irr; // GLM without template namespace MiniGLM { // ------------------------------------------------------------------------ inline float overflow() { volatile float f = 1e10; for (int i = 0; i < 10; i++) f *= f; // this will overflow before the for loop terminates return f; } // overflow // ------------------------------------------------------------------------ inline float toFloat32(short value) { int s = (value >> 15) & 0x00000001; int e = (value >> 10) & 0x0000001f; int m = value & 0x000003ff; if (e == 0) { if (m == 0) { // // Plus or minus zero // uint32_t tmp_data = (unsigned int)(s << 31); float ret; memcpy(&ret, &tmp_data, 4); return ret; } else { // // Denormalized number -- renormalize it // while(!(m & 0x00000400)) { m <<= 1; e -= 1; } e += 1; m &= ~0x00000400; } } else if (e == 31) { if (m == 0) { // // Positive or negative infinity // uint32_t tmp_data = (unsigned int)((s << 31) | 0x7f800000); float ret; memcpy(&ret, &tmp_data, 4); return ret; } else { // // Nan -- preserve sign and significand bits // uint32_t tmp_data = (unsigned int)((s << 31) | 0x7f800000 | (m << 13)); float ret; memcpy(&ret, &tmp_data, 4); return ret; } } // // Normalized number // e = e + (127 - 15); m = m << 13; // // Assemble s, e and m. // uint32_t tmp_data = (unsigned int)((s << 31) | (e << 23) | m); float ret; memcpy(&ret, &tmp_data, 4); return ret; } // toFloat32 // ------------------------------------------------------------------------ inline short toFloat16(float const & f) { int i; memcpy(&i, &f, 4); // // Our floating point number, f, is represented by the bit // pattern in integer i. Disassemble that bit pattern into // the sign, s, the exponent, e, and the significand, m. // Shift s into the position where it will go in in the // resulting half number. // Adjust e, accounting for the different exponent bias // of float and half (127 versus 15). // int s = (i >> 16) & 0x00008000; int e = ((i >> 23) & 0x000000ff) - (127 - 15); int m = i & 0x007fffff; // // Now reassemble s, e and m into a half: // if (e <= 0) { if (e < -10) { // // E is less than -10. The absolute value of f is // less than half_MIN (f may be a small normalized // float, a denormalized float or a zero). // // We convert f to a half zero. // return short(s); } // // E is between -10 and 0. F is a normalized float, // whose magnitude is less than __half_NRM_MIN. // // We convert f to a denormalized half. // m = (m | 0x00800000) >> (1 - e); // // Round to nearest, round "0.5" up. // // Rounding may cause the significand to overflow and make // our number normalized. Because of the way a half's bits // are laid out, we don't have to treat this case separately; // the code below will handle it correctly. // if (m & 0x00001000) m += 0x00002000; // // Assemble the half from s, e (zero) and m. // return short(s | (m >> 13)); } else if (e == 0xff - (127 - 15)) { if (m == 0) { // // F is an infinity; convert f to a half // infinity with the same sign as f. // return short(s | 0x7c00); } else { // // F is a NAN; we produce a half NAN that preserves // the sign bit and the 10 leftmost bits of the // significand of f, with one exception: If the 10 // leftmost bits are all zero, the NAN would turn // into an infinity, so we have to set at least one // bit in the significand. // m >>= 13; return short(s | 0x7c00 | m | (m == 0)); } } else { // // E is greater than zero. F is a normalized float. // We try to convert f to a normalized half. // // // Round to nearest, round "0.5" up // if (m & 0x00001000) { m += 0x00002000; if (m & 0x00800000) { m = 0; // overflow in significand, e += 1; // adjust exponent } } // // Handle exponent overflow // if (e > 30) { overflow(); // Cause a hardware floating point overflow; return short(s | 0x7c00); // if this returns, the half becomes an } // infinity with the same sign as f. // // Assemble the half from s, e and m. // return short(s | (e << 10) | (m >> 13)); } } // toFloat16 // ------------------------------------------------------------------------ inline uint32_t normalizedSignedFloatsTo1010102 (const std::array& src, int extra_2_bit = -1) { int part = 0; uint32_t packed = 0; float v = fminf(1.0f, fmaxf(-1.0f, src[0])); if (v > 0.0f) { part = (int)((v * 511.0f) + 0.5f); } else { part = (int)((v * 512.0f) - 0.5f); } packed |= ((uint32_t)part & 1023) << 0; v = fminf(1.0f, fmaxf(-1.0f, src[1])); if (v > 0.0f) { part = (int)((v * 511.0f) + 0.5f); } else { part = (int)((v * 512.0f) - 0.5f); } packed |= ((uint32_t)part & 1023) << 10; v = fminf(1.0f, fmaxf(-1.0f, src[2])); if (v > 0.0f) { part = (int)((v * 511.0f) + 0.5f); } else { part = (int)((v * 512.0f) - 0.5f); } packed |= ((uint32_t)part & 1023) << 20; if (extra_2_bit >= 0) { part = extra_2_bit; } else { part = (int)(-0.5f); } packed |= ((uint32_t)part & 3) << 30; return packed; } // normalizedSignedFloatsTo1010102 // ------------------------------------------------------------------------ inline std::array vertexType2101010RevTo4HF(uint32_t packed) { std::array ret; int part = packed & 1023; if (part & 512) { ret[0] = (float)(1024 - part) * (-1.0f / 512.0f); } else { ret[0] = (float)part * (1.0f / 511.0f); } part = (packed >> 10) & 1023; if (part & 512) { ret[1] = (float)(1024 - part) * (-1.0f / 512.0f); } else { ret[1] = (float)part * (1.0f / 511.0f); } part = (packed >> 20) & 1023; if (part & 512) { ret[2] = (float)(1024 - part) * (-1.0f / 512.0f); } else { ret[2] = (float)part * (1.0f / 511.0f); } part = (packed >> 30) & 3; if (part & 2) { ret[3] = (float)(4 - part) * (-1.0f / 2.0f); } else { ret[3] = (float)part; } std::array result; for (int i = 0; i < 4; i++) { result[i] = toFloat16(ret[i]); } return result; } // vertexType2101010RevTo4HF // ------------------------------------------------------------------------ inline std::array extractNormalizedSignedFloats(uint32_t packed, bool calculate_w = false) { std::array ret = {}; int part = packed & 1023; if (part & 512) { ret[0] = (float)(1024 - part) * (-1.0f / 512.0f); } else { ret[0] = (float)part * (1.0f / 511.0f); } part = (packed >> 10) & 1023; if (part & 512) { ret[1] = (float)(1024 - part) * (-1.0f / 512.0f); } else { ret[1] = (float)part * (1.0f / 511.0f); } part = (packed >> 20) & 1023; if (part & 512) { ret[2] = (float)(1024 - part) * (-1.0f / 512.0f); } else { ret[2] = (float)part * (1.0f / 511.0f); } if (calculate_w) { float inv_sqrt_2 = 1.0f / sqrtf(2.0f); ret[0] *= inv_sqrt_2; ret[1] *= inv_sqrt_2; ret[2] *= inv_sqrt_2; float largest_val = sqrtf(fmaxf(0.0f, 1.0f - (ret[0] * ret[0]) - (ret[1] * ret[1]) - (ret[2] * ret[2]))); part = (packed >> 30) & 3; switch(part) { case 0: { auto tmp = ret; ret[0] = largest_val; ret[1] = tmp[0]; ret[2] = tmp[1]; ret[3] = tmp[2]; break; } case 1: { auto tmp = ret; ret[0] = tmp[0]; ret[1] = largest_val; ret[2] = tmp[1]; ret[3] = tmp[2]; break; } case 2: { auto tmp = ret; ret[0] = tmp[0]; ret[1] = tmp[1]; ret[2] = largest_val; ret[3] = tmp[2]; break; } case 3: ret[3] = largest_val; break; default: assert(false); break; } } return ret; } // extractNormalizedSignedFloats // ------------------------------------------------------------------------ // Please normalize vector before compressing // ------------------------------------------------------------------------ inline uint32_t compressVector3(const irr::core::vector3df& vec) { return normalizedSignedFloatsTo1010102({{vec.X, vec.Y, vec.Z}}); } // compressVector3 // ------------------------------------------------------------------------ inline core::vector3df decompressVector3(uint32_t packed) { const std::array out = extractNormalizedSignedFloats(packed); core::vector3df ret(out[0], out[1], out[2]); return ret.normalize(); } // decompressVector3 // ------------------------------------------------------------------------ inline uint32_t compressQuaternion(const btQuaternion& q) { const float length = q.length(); assert(length != 0.0f); std::array tmp_2 = {{ q.x() / length, q.y() / length, q.z() / length, q.w() / length }}; std::array tmp_3 = {}; auto ret = std::max_element(tmp_2.begin(), tmp_2.end(), [](float a, float b) { return std::abs(a) < std::abs(b); }); int extra_2_bit = int(std::distance(tmp_2.begin(), ret)); float sqrt_2 = sqrtf(2.0f); switch (extra_2_bit) { case 0: { float neg = tmp_2[0] < 0.0f ? -1.0f : 1.0f; tmp_3[0] = tmp_2[1] * neg * sqrt_2; tmp_3[1] = tmp_2[2] * neg * sqrt_2; tmp_3[2] = tmp_2[3] * neg * sqrt_2; break; } case 1: { float neg = tmp_2[1] < 0.0f ? -1.0f : 1.0f; tmp_3[0] = tmp_2[0] * neg * sqrt_2; tmp_3[1] = tmp_2[2] * neg * sqrt_2; tmp_3[2] = tmp_2[3] * neg * sqrt_2; break; } case 2: { float neg = tmp_2[2] < 0.0f ? -1.0f : 1.0f; tmp_3[0] = tmp_2[0] * neg * sqrt_2; tmp_3[1] = tmp_2[1] * neg * sqrt_2; tmp_3[2] = tmp_2[3] * neg * sqrt_2; break; } case 3: { float neg = tmp_2[3] < 0.0f ? -1.0f : 1.0f; tmp_3[0] = tmp_2[0] * neg * sqrt_2; tmp_3[1] = tmp_2[1] * neg * sqrt_2; tmp_3[2] = tmp_2[2] * neg * sqrt_2; break; } default: assert(false); break; } return normalizedSignedFloatsTo1010102(tmp_3, extra_2_bit); } // compressQuaternion // ------------------------------------------------------------------------ inline uint32_t compressIrrQuaternion(const core::quaternion& q) { return compressQuaternion(btQuaternion(q.X, q.Y, q.Z, q.W)); } // ------------------------------------------------------------------------ inline core::quaternion decompressQuaternion(uint32_t packed) { const std::array out = extractNormalizedSignedFloats(packed, true/*calculate_w*/); core::quaternion ret(out[0], out[1], out[2], out[3]); return ret.normalize(); } // decompressQuaternion // ------------------------------------------------------------------------ inline btQuaternion decompressbtQuaternion(uint32_t packed) { const std::array out = extractNormalizedSignedFloats(packed, true/*calculate_w*/); btQuaternion ret(out[0], out[1], out[2], out[3]); return ret.normalize(); } // decompressbtQuaternion // ------------------------------------------------------------------------ inline std::array getQuaternionInternal(const core::matrix4& m) { btVector3 row[3]; memcpy(&row[0][0], &m[0], 12); memcpy(&row[1][0], &m[4], 12); memcpy(&row[2][0], &m[8], 12); std::array q; float root = row[0].x() + row[1].y() + row[2].z(); const float trace = root; if (trace > 0.0f) { root = sqrtf(trace + 1.0f); q[3] = 0.5f * root; root = 0.5f / root; q[0] = root * (row[1].z() - row[2].y()); q[1] = root * (row[2].x() - row[0].z()); q[2] = root * (row[0].y() - row[1].x()); } else { static int next[3] = {1, 2, 0}; int i = 0; int j = 0; int k = 0; if (row[1].y() > row[0].x()) { i = 1; } if (row[2].z() > row[i][i]) { i = 2; } j = next[i]; k = next[j]; root = sqrtf(row[i][i] - row[j][j] - row[k][k] + 1.0f); q[i] = 0.5f * root; root = 0.5f / root; q[j] = root * (row[i][j] + row[j][i]); q[k] = root * (row[i][k] + row[k][i]); q[3] = root * (row[j][k] - row[k][j]); } return q; } // ------------------------------------------------------------------------ inline core::quaternion getQuaternion(const core::matrix4& m) { std::array q = getQuaternionInternal(m); return core::quaternion(q[0], q[1], q[2], q[3]).normalize(); } // ------------------------------------------------------------------------ inline btQuaternion getBulletQuaternion(const core::matrix4& m) { std::array q = getQuaternionInternal(m); return btQuaternion(q[0], q[1], q[2], q[3]).normalize(); } // ------------------------------------------------------------------------ inline uint32_t quickTangent(uint32_t packed_normal) { core::vector3df normal = decompressVector3(packed_normal); core::vector3df tangent; core::vector3df c1 = normal.crossProduct(core::vector3df(0.0f, 0.0f, 1.0f)); core::vector3df c2 = normal.crossProduct(core::vector3df(0.0f, 1.0f, 0.0f)); if (c1.getLengthSQ() > c2.getLengthSQ()) { tangent = c1; } else { tangent = c2; } tangent.normalize(); // Assume bitangent sign is positive 1.0f return compressVector3(tangent) | 1 << 30; } // quickTangent // ------------------------------------------------------------------------ /** Round and save compressed values (optionally) btTransform. * It will round with 2 digits with min / max +/- 2^23 / 100 for origin in * btTransform and call compressQuaternion above to compress the rotation * part, if compressed_data is provided, 3 24 bits and 1 32 bits of * compressed data will be written in an int[4] array. */ inline void compressbtTransform(btTransform& cur_t, int* compressed_data = NULL) { int x = (int)(cur_t.getOrigin().x() * 100.0f); int y = (int)(cur_t.getOrigin().y() * 100.0f); int z = (int)(cur_t.getOrigin().z() * 100.0f); x = core::clamp(x, -0x800000, 0x7fffff); y = core::clamp(y, -0x800000, 0x7fffff); z = core::clamp(z, -0x800000, 0x7fffff); uint32_t compressed_q = compressQuaternion(cur_t.getRotation()); cur_t.setOrigin(btVector3( (float)x / 100.0f, (float)y / 100.0f, (float)z / 100.0f)); cur_t.setRotation(decompressbtQuaternion(compressed_q)); if (compressed_data) { compressed_data[0] = x; compressed_data[1] = y; compressed_data[2] = z; compressed_data[3] = (int)compressed_q; } } // compressbtTransform // ------------------------------------------------------------------------ inline btTransform decompressbtTransform(int* compressed_data) { btTransform trans; trans.setOrigin(btVector3( (float)compressed_data[0] / 100.0f, (float)compressed_data[1] / 100.0f, (float)compressed_data[2] / 100.0f)); trans.setRotation(decompressbtQuaternion( (uint32_t)compressed_data[3])); return trans; } // decompressbtTransform // ------------------------------------------------------------------------ void unitTesting(); } #endif