, я написал собственные векторные классы, такие как vec2
, vec3
и т.д., которые имитируют типы GLSL и выглядят примерно так:
struct vec3
{
inline vec3(float x, float y, float z)
: x(x), y(y), z(z) {}
union { float x, r, s; };
union { float y, g, t; };
union { float z, b, p; };
};
Операции с векторами реализуются следующим образом:
inline vec3 operator +(vec3 a, vec3 b)
{
return vec3(a.x + b.x, a.y + b.y, a.z + b.z);
}
Это позволяет мне создавать векторы и получать доступ к их компонентам, используя синтаксис, подобный GLSL, и выполнять операции над ними почти так же, как если бы они были числовыми типами. Объединения позволяют мне безразлично ссылаться на первую координату как x
или как r
, как в случае с GLSL. Например:
vec3 point = vec3(1.f, 2.f, 3.f);
vec3 other = point + point;
point.x = other.b;
Но GLSL также допускает swizzling доступ, даже с дырами между компонентами. Например, p.yx
ведет себя как vec2
с замененными местами p
s x
и y
. Если ни один компонент не повторяется, это также lvalue. Некоторые примеры:
other = point.xyy; /* Note: xyy, not xyz */
other.xz = point.xz;
point.xy = other.xx + vec2(1.0f, 2.0f);
Теперь это можно сделать с помощью стандартных геттеров и сеттеров, таких как vec2 xy ()
и void xy (vec2 val)
. Это то, что делает библиотека GLM .
Однако я разработал этот шаблон, который позволяет мне делать то же самое в C ++. Поскольку все является POD-структурой, я могу добавить больше объединений:
template struct MagicVec2
{
friend struct vec2;
inline vec2 operator =(vec2 that);
private:
float ptr[1 + (I > J ? I : J)];
};
template
inline vec2 MagicVec2::operator =(vec2 that)
{
ptr[I] = that.x; ptr[J] = that.y;
return *this;
}
и например. класс vec3
становится (я немного упростил, например, ничто не мешает использовать xx
здесь как lvalue):
struct vec3
{
inline vec3(float x, float y, float z)
: x(x), y(y), z(z) {}
template
inline vec3(MagicVec3 const &v)
: x(v.ptr[I]), y(v.ptr[J]), z(v.ptr[K]) {}
union
{
struct { float x, y, z; };
struct { float r, g, b; };
struct { float s, t, p; };
MagicVec2<0,0> xx, rr, ss;
MagicVec2<0,1> xy, rg, st;
MagicVec2<0,2> xz, rb, sp;
MagicVec2<1,0> yx, gr, ts;
MagicVec2<1,1> yy, gg, tt;
MagicVec2<1,2> yz, gb, tp;
MagicVec2<2,0> zx, br, ps;
MagicVec2<2,1> zy, bg, pt;
MagicVec2<2,2> zz, bb, pp;
/* Also MagicVec3 and MagicVec4, of course */
};
};
В основном: я использую объединение для смешайте компоненты вектора с плавающей запятой с магическим объектом, который на самом деле не является vec2
, но может быть неявно преобразован в vec2
(потому что существует конструктор vec2
, позволяющий it), и ему может быть присвоено значение vec2
(из-за его перегруженного оператора присваивания).
Результатом очень доволен. Приведенный выше код GLSL работает, и я считаю, что получаю приличную безопасность типов. И я могу # включить
шейдер GLSL в свой код C ++.
Конечно, есть ограничения. Я знаю следующие:
sizeof (point.xz)
будет 3 * sizeof (float)
вместо ожидаемого 2 * sizeof (float)
. Это сделано намеренно, и я не знаю, может ли это быть проблемой. & foo.xz
не может использоваться как vec2 *
. Это должно быть нормально, потому что я всегда передаю эти объекты только по значению. Итак, мой вопрос: что я мог упустить из виду, что усложнит мою жизнь с этим шаблоном? Кроме того, я еще нигде не нашел этот шаблон, поэтому, если кто-то знает его название, мне интересно .
Примечание: я хочу придерживаться C ++ 98, но я полагаюсь на компилятор, позволяющий набирать типы с помощью объединений.Моя причина, по которой я пока не хочу C ++ 11, - это отсутствие поддержки компилятора на некоторых из моих целевых платформ; все же интересующие меня компиляторы поддерживают каламбур.