#pragma once #include "../math/vector.h" #include #include #include // Recovered Tungsten vertex layout. Normals and tangents use the observed // unsigned-byte [-1, 1] encoding; tangent[3] stores the bitangent handedness. class idDrawVert { public: idVec3 xyz; idVec2 st; std::uint8_t normal[4]; std::uint8_t tangent[4]; std::uint8_t color[4]; idDrawVert& operator=(const idDrawVert& rhs) = default; idVec3 GetNormal() const { return DecodeDirection(normal); } idVec3 GetTangent() const { return DecodeDirection(tangent); } idVec3 GetBiTangent() const { const idVec3 bitangent = GetNormal().Cross(GetTangent()); return tangent[3] >= 128 ? bitangent : -bitangent; } void SetNormal(const idVec3& value) { EncodeDirection(value, normal); } void SetTangent(const idVec3& value) { EncodeDirection(value, tangent); } void SetBiTangent(const idVec3& value) { tangent[3] = value.Dot(GetNormal().Cross(GetTangent())) >= 0.0f ? std::uint8_t(255) : std::uint8_t(0); } void LerpAll(const idDrawVert& a, const idDrawVert& b, const float fraction) { xyz.Set( Lerp(a.xyz.x, b.xyz.x, fraction), Lerp(a.xyz.y, b.xyz.y, fraction), Lerp(a.xyz.z, b.xyz.z, fraction)); st.Set( Lerp(a.st.x, b.st.x, fraction), Lerp(a.st.y, b.st.y, fraction)); const idVec3 interpolatedNormal = Normalize( a.GetNormal() + (b.GetNormal() - a.GetNormal()) * fraction); const idVec3 interpolatedTangent = Normalize( a.GetTangent() + (b.GetTangent() - a.GetTangent()) * fraction); const idVec3 interpolatedBiTangent = Normalize( a.GetBiTangent() + (b.GetBiTangent() - a.GetBiTangent()) * fraction); SetNormal(interpolatedNormal); SetTangent(interpolatedTangent); SetBiTangent(interpolatedBiTangent); for (int component = 0; component < 4; ++component) { const int value = static_cast( static_cast(a.color[component]) + (static_cast(b.color[component]) - static_cast(a.color[component])) * fraction); color[component] = static_cast( (std::max)(0, (std::min)(255, value))); } } private: static float Lerp(const float a, const float b, const float fraction) { return a + (b - a) * fraction; } static idVec3 Normalize(const idVec3& value) { const float lengthSqr = value.LengthSqr(); if (lengthSqr <= 1.0e-30f) { return idVec3(0.0f, 0.0f, 0.0f); } return value * (1.0f / std::sqrt(lengthSqr)); } static idVec3 DecodeDirection(const std::uint8_t value[4]) { return Normalize(idVec3( static_cast(value[0]) * (2.0f / 255.0f) - 1.0f, static_cast(value[1]) * (2.0f / 255.0f) - 1.0f, static_cast(value[2]) * (2.0f / 255.0f) - 1.0f)); } static void EncodeDirection(const idVec3& input, std::uint8_t value[4]) { const idVec3 direction = Normalize(input); for (int component = 0; component < 3; ++component) { const float scaled = (direction[component] + 1.0f) * 127.5f + 0.5f; value[component] = static_cast( (std::max)(0.0f, (std::min)(255.0f, scaled))); } } }; static_assert(sizeof(idDrawVert) == 32, "Recovered idDrawVert layout changed");