一.前言
- 在写gl shader的时候, 我发现glsl的语法中vec可以支持如同如下的语法任意混合属性
vec3 pos;
pos.xyz
pos.yxz
pos.xxx
pos.zx
...
- 我们可以很容易的在C++写一个2、3个属性的vec类,但是如何使得vec也支持如上语法将属性进行混合呢?
二.方案一
- 我们或许可以想到,利用uion+struct来实现
struct vec3
{
union
{
struct
{
int x,y,z;
};
struct
{
vec2 xy;
int z;
};
struct
{
int x;
vec2 yz;
};
struct
{
vec3 xyz;
};
}
}
- 利用union的特性可以使得多个”名字”使用同一块内存区,然而以上方法并不能很好的实现我们想要的Swizzle。比如,我们不能使用类似于 xz(有间隔)、zy(逆序) 这样的属性。
二.方案二
- 利用宏、将属性转变为方法
#include <iostream>
using namespace std;
class Vec2
{
public:
Vec2(int xx, int yy):x(xx), y(yy){}
int x, y;
};
class Vec2Ref
{
public:
Vec2Ref(int& x, int& y):m_x(x), m_y(y){}
Vec2Ref(const Vec2Ref& v):m_x(v.m_x), m_y(v.m_y){}
void operator=(Vec2 v)
{
m_x = v.x;
m_y = v.y;
}
operator Vec2()
{
return Vec2(m_x, m_y);
}
private:
int& m_x;
int& m_y;
};
class Vec3
{
public:
Vec3(int xx, int yy, int zz):x(xx), y(yy), z(zz){}
int x, y, z;
public:
Vec2Ref FunXY() { return Vec2Ref(x, y);}
Vec2Ref FunYZ() { return Vec2Ref(y, z);}
Vec2Ref FunXZ() { return Vec2Ref(x, z);}
#define xy FunXY()
#define yz FunYZ()
#define xz FunXZ()
};
int main()
{
Vec3 v3(1,2,3);
Vec2 v2(4,5);
cout << "sizeof(Vec3): " << sizeof(Vec3) << endl;
v3.xy = v2;
cout << v3.x << v3.y << v3.z << endl;
return 0;
}
- 利用添加中间层和构造函数和引用成员变量使得可以灵活的重组属性,不过缺点是宏会造成全局污染。其他地方也没办法正常使用x,y这样的变量。
二.方案三
- 使用glm库的时候,发现glm库提供了这样的方法,使得可以对成员的Swizzle。需要添加GLM_SWIZZLE宏。
- 下面这段代码主要Copy自glm库,个人只进行了少量的修改。
template <typename T>
struct _swizzle_base0
{
protected:
inline T& elem(size_t i) { return (reinterpret_cast<T*>(_buffer))[i]; }
inline T const& elem(size_t i) const { return (reinterpret_cast<const T*>(_buffer))[i]; }
char _buffer[1];
};
template <int N, typename T, template <typename> class vecType, int E0, int E1, int E2>
struct _swizzle_base1 : public _swizzle_base0<T>
{
};
template <typename T, template <typename> class vecType, int E0, int E1>
struct _swizzle_base1<2, T, vecType, E0, E1, -1> : public _swizzle_base0<T>
{
inline vecType<T> operator ()() const { return vecType<T>(this->elem(E0), this->elem(E1)); }
};
template <typename T, template <typename> class vecType, int E0, int E1, int E2>
struct _swizzle_base1<3, T, vecType, E0, E1, E2> : public _swizzle_base0<T>
{
inline vecType<T> operator ()() const { return vecType<T>(this->elem(E0), this->elem(E1), this->elem(E2)); }
};
template <int N, typename T, template <typename> class vecType, int E0, int E1, int E2, int DUPLICATE_ELEMENTS>
struct _swizzle_base2 : public _swizzle_base1<N, T, vecType, E0, E1, E2>
{
inline _swizzle_base2& operator= (const T& t)
{
for (int i = 0; i < N; ++i)
(*this)[i] = t;
return *this;
}
# define APPLY_OP(EXP) \
inline void \
operator EXP (vecType<T> const& that) \
{ \
struct op { \
inline void \
operator() (T& e, T& t) \
{ e EXP t; } \
}; \
_apply_op(that, op()); \
}
inline _swizzle_base2& operator= (vecType<T> const& that)
{
struct op {
inline void operator() (T& e, T& t) { e = t; }
};
_apply_op(that, op());
return *this;
}
APPLY_OP(-=)
APPLY_OP(+=)
APPLY_OP(*=)
APPLY_OP(/=)
inline T& operator[](size_t i)
{
const int offset_dst[4] = { E0, E1, E2 };
return this->elem(offset_dst[i]);
}
inline T operator[](size_t i) const
{
const int offset_dst[4] = { E0, E1, E2 };
return this->elem(offset_dst[i]);
}
#undef APPLY_OP
protected:
template <typename U>
inline void _apply_op(vecType<T> const& that, U op)
{
T t[N];
for (int i = 0; i < N; ++i)
t[i] = that[i];
for (int i = 0; i < N; ++i)
op((*this)[i], t[i]);
}
};
template <int N, typename T, template <typename> class vecType, int E0, int E1, int E2>
struct _swizzle_base2<N, T, vecType, E0, E1, E2, 1> : public _swizzle_base1<N, T, vecType, E0, E1, E2>
{
struct Stub {};
inline _swizzle_base2& operator= (Stub const &) { return *this; }
inline T operator[] (size_t i) const
{
const int offset_dst[4] = { E0, E1, E2 };
return this->elem(offset_dst[i]);
}
};
template <int N, typename T, template <typename> class vecType, int E0, int E1, int E2>
struct _swizzle : public _swizzle_base2<N, T, vecType, E0, E1, E2, (E0 == E1 || E0 == E2 || E1 == E2)>
{
typedef _swizzle_base2<N, T, vecType, E0, E1, E2, (E0 == E1 || E0 == E2 || E1 == E2)> base_type;
using base_type::operator=;
inline operator vecType<T>() const { return (*this)(); }
};
#define _SWIZZLE3_2_MEMBERS(T, V, E0,E1,E2) \
struct { _swizzle<2,T, V, 0,0,-1> E0 ## E0; }; \
struct { _swizzle<2,T, V, 0,1,-1> E0 ## E1; }; \
struct { _swizzle<2,T, V, 0,2,-1> E0 ## E2; }; \
struct { _swizzle<2,T, V, 1,0,-1> E1 ## E0; }; \
struct { _swizzle<2,T, V, 1,1,-1> E1 ## E1; }; \
struct { _swizzle<2,T, V, 1,2,-1> E1 ## E2; }; \
struct { _swizzle<2,T, V, 2,0,-1> E2 ## E0; }; \
struct { _swizzle<2,T, V, 2,1,-1> E2 ## E1; }; \
struct { _swizzle<2,T, V, 2,2,-1> E2 ## E2; };
#define _SWIZZLE3_3_MEMBERS(T, V ,E0,E1,E2) \
struct { _swizzle<3, T, V, 0,0,0> E0 ## E0 ## E0; }; \
struct { _swizzle<3, T, V, 0,0,1> E0 ## E0 ## E1; }; \
struct { _swizzle<3, T, V, 0,0,2> E0 ## E0 ## E2; }; \
struct { _swizzle<3, T, V, 0,1,0> E0 ## E1 ## E0; }; \
struct { _swizzle<3, T, V, 0,1,1> E0 ## E1 ## E1; }; \
struct { _swizzle<3, T, V, 0,1,2> E0 ## E1 ## E2; }; \
struct { _swizzle<3, T, V, 0,2,0> E0 ## E2 ## E0; }; \
struct { _swizzle<3, T, V, 0,2,1> E0 ## E2 ## E1; }; \
struct { _swizzle<3, T, V, 0,2,2> E0 ## E2 ## E2; }; \
struct { _swizzle<3, T, V, 1,0,0> E1 ## E0 ## E0; }; \
struct { _swizzle<3, T, V, 1,0,1> E1 ## E0 ## E1; }; \
struct { _swizzle<3, T, V, 1,0,2> E1 ## E0 ## E2; }; \
struct { _swizzle<3, T, V, 1,1,0> E1 ## E1 ## E0; }; \
struct { _swizzle<3, T, V, 1,1,1> E1 ## E1 ## E1; }; \
struct { _swizzle<3, T, V, 1,1,2> E1 ## E1 ## E2; }; \
struct { _swizzle<3, T, V, 1,2,0> E1 ## E2 ## E0; }; \
struct { _swizzle<3, T, V, 1,2,1> E1 ## E2 ## E1; }; \
struct { _swizzle<3, T, V, 1,2,2> E1 ## E2 ## E2; }; \
struct { _swizzle<3, T, V, 2,0,0> E2 ## E0 ## E0; }; \
struct { _swizzle<3, T, V, 2,0,1> E2 ## E0 ## E1; }; \
struct { _swizzle<3, T, V, 2,0,2> E2 ## E0 ## E2; }; \
struct { _swizzle<3, T, V, 2,1,0> E2 ## E1 ## E0; }; \
struct { _swizzle<3, T, V, 2,1,1> E2 ## E1 ## E1; }; \
struct { _swizzle<3, T, V, 2,1,2> E2 ## E1 ## E2; }; \
struct { _swizzle<3, T, V, 2,2,0> E2 ## E2 ## E0; }; \
struct { _swizzle<3, T, V, 2,2,1> E2 ## E2 ## E1; }; \
struct { _swizzle<3, T, V, 2,2,2> E2 ## E2 ## E2; };
template<typename T>
struct vec2
{
union
{
struct {
T x, y;
};
};
vec2(T tx, T ty) :x(tx), y(ty) {}
};
template<typename T>
struct vec3
{
union
{
struct {
T x, y, z;
};
_SWIZZLE3_2_MEMBERS(T, ::vec2, x, y, z)
_SWIZZLE3_3_MEMBERS(T, ::vec3, x, y, z)
};
vec3(T tx, T ty, T tz) :x(tx), y(ty), z(tz) {}
};
int main()
{
vec3<float> v(1, 2, 3);
vec3<float> v3 = v.yzy;
printf("%f %f %f\n", v3.x, v3.y, v3.z);
getchar();
return 0;
}
就以上的代码,做简要的分析。
1. class _swizzle_base0
_swizzle_base0 作为 swizzle 最底层的类仅仅是提供elem接口,利用 i 来获取第 i 个 element。
2. class _swizzle_base1 : public _swizzle_base0
_swizzle_base1<N, T, VecType, E0, E1, E2>
N: swizzle的属性的个数
T: 属性类型
VecType: Swizzle elem后的目标类型
E0,E1,E2: Swizzle 的每个elem的索引
_swizzle_base1 以N的个数来做特化,主要是为 swizzle 属性 提供 get 属性。
也就是说当我们使用 vec2 v2 = v3.xz 的时候实则是调用了 _swizzle_base1::operator()() 来构造了一个vec2。
根据N分别为2,3来构造由2个elem或者3个elem swizzle的VecType.
3. class _swizzle_base2 : public _swizzle_base1
_swizzle_base2<N, T, VecType, E0, E1, E2, DUPLICATE_ELEMENTS>
DUPLICATE_ELEMENTS: 这个参数是指swizzle的属性中是否有相同的,比如: vec3.yxy
事实上,当swizzle的属性中有相同的elem时,只有get属性,因为当对其赋值时vec3.yxy = (1,2,3)行为是不确定的。
_swizzle_base2 以 DUPLICATE_ELEMENTS(0,1)特化
当DUPLICATE_ELEMENTS为0时,也就是无相同元素,有set属性。
_swizzle_base2::operator=(vecType<T> const& )
_swizzle_base2::operator+=(vecType<T> const& )
_swizzle_base2::operator-=(vecType<T> const& )
_swizzle_base2::operator*=(vecType<T> const& )
_swizzle_base2::operator/=(vecType<T> const& )
以上方法提供了set属性,
也就是说当vec3.zy = vec2(2,1)的时候实则调用了_swizzle_base2::operator=(vecType<T> const&)
_swizzle_base2::operator[](size_t i)
提供的是按照索引访问swizzle之后的属性,注意区别和_swizzle_base0::elem的区别。
4. class _swizzle : public _swizzle_base2
_swizzle<N, T, VecType, E0, E1, E2>
提供_swizzle::operator vecType<T>()转换函数,根据特化调用基类的operator()得到相应的swizzle属性。
5. uinon + struct + 宏
利用宏完成对属性swizzle的排列组合
_SWIZZLE3_2_MEMBERS(T, ::vec2, x, y, z)
只需如上申明,就可以完成对x,y,z三个属性的任意两个元素的swizzle。