It prevents unwanted implicit conversions from e.g. nullptr to Camera,
Vector2 to Physics::Point etc. By making all the constructors explicit
it is easier to routinely add the keyword to all new classes instead of
thinking about cases when to add and when not to.
Currently, when accidentaly creating specialized Vector from smaller
number of components than required, the error message isn't really
helpful, as it stops on static assert on wrong number of arguments
passed to RectangularMatrix:
Vector3(0, 1); // static assert: wrong number of arguments passed to
// RectangularMatrix<1, 2> -- wtf?!
Now the first argument is Vector2/Vector3 instead of Vector<2>/Vector<3>
and the error message now properly states that no matching constructor
was found.
Vector4 doesn't set W component to one by default anymore, this is now
handled by Point*D itself. This finally allows creating of 2D primitives
and 2D position vectors without messing explicitly with Z = 1.
All classes which should use Point instead of Vector were updated to use
Point instead.
Before it has to be done with overly verbose and cumbersome Java-style:
Vector4 vec;
vec.setX(vec.x() + 5);
vec = Vector4(vec.xyz()*2, vec.w());
Now it can be done this way:
vec.x() += 5;
vec.xyz() *= 2;
Currently moved only non-square functionality from Matrix there. Also
static constant members such as row/column count and size are now
lowercase, as they are variables, not types.
They can be used for both floating-point (normalized) and integral
(denormalized) representation and support conversion between them.
HSV conversion is done always using floating-point types.
Getters for RGB(A) were removed from Vector3 and Vector4, they are now
part of Color3 and Color4. Because of this, Framebuffer now accepts
Color instead of Vector.
It shouldn't be implicit, because then it will be possible to
autoconvert Vector4 from Vector2, which shouldn't be possible at all.
But it shouldn't be explicit either, because this will not be possible
then:
Vector2 vec2;
Vector3 vec3 = {vec2, 1.0};
Vector4 constructor from Vector3 stays the same, because the conversion
is fairly common, nearly always with W set to 1.
Each function which returned e.g. Vector<size, T> was in subclasses
overloaded with function returning e.g. Vector3<T>, so the user is able
to use subclass-specific functions. It was nightmare to maintain and it
cluttered the documentation a lot.
Long-standing TODO. It is better to have size first, because it is more
significant than type (e.g. because there are Vector4<T> specializations
and not VectorT<4> specializations). It is also IMHO easier for user to
distinguish/read the type than before:
Vector<float, 4> -> Vector4<float> // before
Vector<4, float> -> Vector4<float> // now
Because no operator= which took the class itself as argument (parent
class only), the compiler generated default assignment and assignment
move constructors, e.g.
Vector3<T>& operator=(const Vector3<T>&);
Vector3<T>& operator=(Vector3&&);
Resulting in conflicts when using assignment uniform initialization,
i.e. it wasn't possible to do things like this, but that's now fixed:
Vector3<int> vec;
vec = {0, 1, 2};
Other functions left untouched (they are still taking e.g. Vector<T,
3> instead of Vector3<T>), because it saves one useless dummy
constructor call (which would be visible in profiler, but without
having any performance impacts altogether).
Removed functions at(), set() and add(), everything (and more) can be
now done using operator[]. Accessing matrix elements is now done through
column vectors, e.g.:
Matrix4 a;
a.at(row, col); // before
a[col][row]; // now
Note that because operator[] on Matrix returns column vector (there is
nothing like row vector), the parameter "order" is now swapped.
Marked the constructor as explicit, because we don't want mistakes like
this to happen:
Matrix4::rotation(1.0f, deg(3.0f)); // oops, swapped axis and angle!
Instead, when calling such constructor, the type must be said
explicitly (initializer-list is forbidden, too):
Matrix4::rotation(deg(3.0f), Vector3(1.0f)); // okay
It should improve performance, because what can be computed at compile
time is now computed at compile time. In addition these things have been
changed or improved:
* Removed constructors from T*, as they cannot be AFAIK written using
constexpr functions only and they only do unnecessary data copying
most of the time. The functionality is now provided using static
functions Matrix*::from() and Vector*::from() which returns either
const or non-const reference to original data, so no copy is
performed. These functions also have explicit warning about unsafe
operations.
* Defaulted copy constructors and assignment operators, using
"default initialization" for arrays instead of memsetting it with
zeros. It should behave the same and this way we don't need any
memcpy(), memset() etc. from <cstring>, which is good.
Multiplication, division, addition and substraction is now done
primarily in assigning operators, as they do the computations in-place,
so they are more memory efficient than implementing them using e.g.
`*this = *this+other;`.
Updated unit test only for matrix multiplication, as vector unit tests
test arithmetic asssign operators too.
Now you can write e.g.
Matrix4::translation(Vector3::xAxis(5.0f));
instead of these:
Matrix4::translation(Vector3::xAxis()*5.0f); // slow!
Matrix4::translation({5.0f, 0.0f, 0.0f}); // boring!