If you have used OpenGL since version 2.0, you should be familiar with GLSL, the incidentally redundant 'Graphics Language Shading Language' (technically 'OpenGL Shading Language', but the "GL" stands for the above, sooooo...).
A nifty feature of GLSL is that it's primitive* linear algebra types, vectors, can use something called 'swizzling' to return a new primitive from the arbitrarily rearranged components of the first. If you have the 3 component vector "vector1", for example, you can return a vector with the X and Z components swapped thusly:
vector1.zyx;
You can also construct vectors of a different order, that is, a different number of components:
vector1.zzyx;
This is really handy in shaders and linear algebra in general because you sometimes need to do just that, such as switching between coordinate systems.
But of course, C++ does not have this feature itself. Not that it should, mind you; it is not a linear algebra language. But when I was working on my own mathematics library for my work on computer graphics, I figured that if my vector package was to be really useful I should try to implement something like swizzling.
That is really difficult, as it turns out. You cannot use the exact same syntax as GLSL and have something sensible; doing so would require all vector objects to store within themselves a copy of every combination and permutation of their four components as a field, which comes out to something like 4^4 == 256 additional float's per vector. Memory is cheap these days, but it is not that cheap, not to mention its nauseating.
Some malign the option, but in C++ you can (and should) overload operators for your classes. Thus, you could use the functional or subscripting operators, () and [], respectively, and have the swizzle combinations as separate, static objects with a name corresponding to the combination. Something like:
/** ... */
static SwizzleClass xyzz;
/** ... */
class Vec4 {
/** ... */
Vec4 operator() ( const SwizzleClass swizz ){ /**...*/ }
/** ... */
};
Which would be sufficient and replicate the functionality as is. However, I found this method ugly; it felt too much like shoehorning something foreign into C++, the kind of thing a good Lisp programmer might pull if they tried to implement closures into C++ without thinking about it too much. I was also a little worried about memory swapping; with some 256 static memory objects flying around, it sounded like a recipe for a speed bump**.
I also realized that there was something else I could make it do that GLSL could not: negate individual components. The syntax in GLSL precludes this because it just stings component names together. So I instead opted for using the functional operator with multiple arguments. These arguments would be of a Swizzle class and have the negation operator overloaded so that one may write:
vector1( x, -y, y );
The number of arguments dictates the return type of the operator.  This method also solves a crucial typing issue: a two component vector cannot use swizzles for components it does not have, such as Z or W, but static, named permutations do not easily allow for this check at compile time and instead we would have to either throw exceptions or use a confusing system of classes and a royal mess of operator overloads. That makes debugging linear algebra code blocks very difficult. Using individual component swizzles like this allows us to solve that problem using the type system more directly, but the solution itself is a little unintuitive and will be the subject of a later post.
*To be clear, 'primitive' in the programming sense, that is, it is a feature of the core language and not a class or type implemented in it.
**The rule is "profile, then optimize". I never profiled this option because it was a hassle to write all those permutations up when I was certain it was a bad idea for any number of reasons, memory being only one of them. It is likely it would not have had dire implications as far as memory goes.
P.S. The HTML generated by this editor is a train wreck when it comes to changing fonts.
No comments:
Post a Comment