我试图为一个Mesh类一般编程。 (我需要CPU转换来变形网格,这就是为什么这个不是在GPU中完成的原因,以防你想要问)
A Mesh<T>
包含3d形状的顶点,以及面部绑定(哪些顶点连接在一起以制作三角形)。
这里T
是顶点的类型,某些类型的模型具有PNCT类型(位置,法线,颜色,texcoord),而其他类型只是PC(位置,颜色)。
struct VertexPNCT {
Vector3 pos, normal ;
Vector4 color ;
Vector2 tex ;
} ;
struct VertexPC {
Vector3 pos ;
Vector4 color ;
} ;
当然,所有顶点都有位置!
但这是我的问题。
Mesh<T>
必须实施transform
方法。当然,每个顶点格式都有一个位置,如果它的名称一致(.pos
),那么该方法可以正常工作:
由于每个顶点肯定都有一个位置,如果我总是在.pos
结构中调用该成员Vertex*
,那么我可以将方法.transform
添加到模板化的类{{1 as:
Mesh<T>
现在这里搞砸了。 如果顶点格式中有一个法线,那么也必须转换该法线(通过“普通矩阵”)。现在我的模板中有一个“if语句”?我必须为每个顶点类型编写一个模板特化void transform( Matrix4& mat )
{
each vertex
vertex.pos = mat * vertex.pos ; // transform it
}
方法(基本上分为2类,有法线的那些,没有法线的那些)?
我在这里滥用了模板吗?错过了一些“编程船”?
这是我真正想要逻辑地做的事情:
.transform
*假设转换中没有比例 (那么你不需要使用“普通矩阵”)
答案 0 :(得分:3)
如果您可以编写Vertex
的层次结构,其中基数只包含位置(以及.pos
成员),则VertexWNormal
会保留.normal
,然后其余的继承从那里,你可以只添加非模板化的重载并让编译器处理:
void transform( Matrix4& m, VertexBase& v ) {
// transform only pos
}
void transform( Matrix4& m, VertexWNormal& v ) {
transform(m,static_cast<VertexBase&>(v));
// transform normal here
}
void tranform( Matrix4& m ) {
foreach vertex:
transform(m,vertex);
}
当然,这对你的设计是否有意义取决于很多事情,你没有表现出很难分辨。
答案 1 :(得分:2)
如果您只有两种类型的顶点,我建议您执行上面描述的David:只需创建两个执行转换的函数,并使用基于顶点类型的重载调用它们。这也适用于更多的顶点类型,但每次添加新的顶点类型时,都需要再次重载该函数。这对于像这里描述的简单函数来说可能没问题,但如果函数实际上更复杂,它可能会变得烦人。
部分修复是创建一个traits类,它告诉特定顶点类型是否具有普通成员。可以设置默认值,使得在大多数情况下它是正确的,并且可以选择基于特征的合适函数。您仍然会提供两个版本的代码,但是对于每个额外的顶点类型,所有需要的是定义特征:
template <typename> struct VertexHasNormal { enum { value = false }; };
template <> struct VertexHasNormal<VertexPNCT> { enum { value = true }; };
template <typename V, template T, template S>
void transform(V& vertices, Matrix4& m, T S::*member) {
for (auto& v: vertices) {
v.*member = mat * v.*member;
}
}
template <bool, typename T>
struct Transform {
template <typename V>
void call(V& vertices, Matrix4& m) {
transform(vertices, m, &T::pos);
}
};
template <typename T>
struct Transform<bool, T> {
template <typename V>
void call(V& vertices, Matrix4& m) {
transform(vertices, m, &T::pos);
transform(vertices, m, &T::normal);
}
};
template <typename T>
void Mash<T>::transform(Matrix4& m) {
Transform<VertexHasNormal::value>::call(this->vertices, m);
}
函数模板transform()
是对顶点序列进行实际转换的函数。我把它考虑在内是因为我认为可以将它考虑在内,但它不需要被考虑在内。它也不需要使用指向成员的指针auto
等。Transform
类型只是一种辅助类型,因为函数模板不能部分专门化。它专门研究顶点类型是否具有normal
成员的特征。最后,Mash<T>::transform()
只会调度到特殊功能的相应版本。
唯一需要的是在定义了具有normal
成员的另一个顶点时添加新的特征专门化。这可能并不可取。在这种情况下,可以使用类型特征确定结构是否具有名为normal
的可访问数据成员。但是,我不认为我可以从头脑中输入这个。实现它的基本思想是利用替换失败不是错误的事实(“
SFINAE“)并且如果测试类型确实具有必要的成员,可以设置两个潜在成员之间的歧义。有一个Boost组件可以做到这一点但是如果你需要自己创建它,它大约是10行代码。
答案 2 :(得分:1)
你可能想要创建结构类并从两个基类派生它们,一个是普通的,一个是没有。然后,您可以使用模板特化来在这两个基类(而不是所有顶点类)之间进行选择。