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 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | template <int T> union vec { real32 e[T]; real32& operator[](int index) { return e[index]; } const real32& operator[](int index) const { return e[index]; } }; template<> union vec<2> { struct { real32 x, y; }; real32 e[2]; }; template<> union vec<3> { struct { real32 x, y, z; }; struct { real32 r, g, b; }; struct { vec<2> xy; real32 pad_; }; real32 e[3]; }; template<> union vec<4> { struct { real32 x, y, z, w; }; struct { real32 r, g, b, a; }; struct { vec<3> xyz; real32 pad_; }; real32 e[4]; }; typedef vec<2> vec2_t; typedef vec<3> vec3_t; typedef vec<4> vec4_t; #if 0 //!!! DANGER !!! template <int T> vec<T> Vec(real32 e, ...) { vec<T> result; va_list args; va_start(args, e); result.e[0] = e; for (int i = 1; i < T; ++i) { result.e[i] = (real32)va_arg(args, real64); } va_end(args); return result; } #endif template <int T, class... Args> vec<T> Vec(Args... args) { const int length = sizeof...(args); static_assert(length == T, ""); vec<T> result = {static_cast<real32>(args)...}; return result; } template <int T> vec<T> operator*(real32 scalar, vec<T> v) { vec<T> result; for (int i = 0; i < T; ++i) { result.e[i] = scalar * v.e[i]; } return result; } template <int T> vec<T> operator*(vec<T> v, real32 scalar) { vec<T> result = scalar * v; return result; } template <int T> vec<T>& operator*=(vec<T>& v, real32 scalar) { v = scalar * v; return v; } template <int T> vec<T> operator-(vec<T> v) { vec<T> result; for (int i = 0; i < T; ++i) { result.e[i] = -v.e[i]; } return result; } template <int T> vec<T> operator+(vec<T> A, vec<T> B) { vec<T> result; for (int i = 0; i < T; ++i) { result.e[i] = A.e[i] + B.e[i]; } return result; } template <int T> vec<T>& operator+=(vec<T>& A, vec<T> B) { A = A + B; return A; } template <int T> vec<T> operator-(vec<T> A, vec<T> B) { vec<T> result; for (int i = 0; i < T; ++i) { result.e[i] = A.e[i] - B.e[i]; } return result; } template <int T> vec<T>& operator-=(vec<T>& A, vec<T> B) { A = A - B; return A; } template <int T> vec<T> Hadamard(vec<T> A, vec<T> B) { vec<T> result; for (int i = 0; i < T; ++i) { result.e[i] = A.e[i] * B.e[i]; } return result; } template <int T> real32 Dot(vec<T> A, vec<T> B) { real32 result = 0.f; for (int i = 0; i < T; ++i) { result += (A.e[i] * B.e[i]); } return result; } template <int T> real32 LengthSq(vec<T> v) { real32 result = Dot(v, v); return result; } template <int T> real32 Length(vec<T> v) { real32 result = Sqrt(LengthSq(v)); return result; } |
One notable thing is that by using template specialization, you can in fact use member accesses like .x, .y, .r, .g, etc. That would have been a deal-breaker for me, because writing
1 | velocity.x; |
is obviously so much more preferable than
1 | velocity.e[0]; |
or
1 | velocity[0]; |
I also included the va_args version of the creator function as an alternative (particularly since some compilers may not yet support variadic templates), but it's marked danger for good reason. It doesn't offer any type-safety for the arguments after the first one, so writing
1 | vec<3> aVec = Vec<3>(1.5f, 2.5f, 6); |
will lead to trouble (i.e. when I tested it, the 6 turned into a 0 so I had {1.5, 2.5, 0}!).
While I haven't tested this, the for loops should be unrolled by any decent compiler since the bounds are known at compile time. All in all, it was pretty straightforward, but in the end I'm not sure this buys us a whole lot. Even though you would just have to add a function once in this version, it is less readable.
In any case, I'm going to keep in my version of HmH because, well.. I took the time to write it. :P
EDIT: I've only tested this using Clang.