为了使代码看起来干净,我在命名空间 Material 中声明了一个类 MaterialTexture 来存储OpenGL的灯光属性。我想在同一名称空间中为此类创建实例(例如 metal , plastic )。 我做了一些谷歌搜索,发现a previous discussion表明数据成员不能在命名空间中分配。建议构造函数初始化数据成员。
但是,在我的案例中有12个数据成员,传递12个参数来构造实例会很繁琐。相反,我想使用三个函数(例如setDiffuse())来设置每个实例的值。
namespace Material
{
class MaterialTexture
{
private:
float diffuse[4];
float specular[4];
float ambient[4];
public:
// Three functions below set the values for data members above.
void setDiffuse(float R, float G, float B, float A);
void setSpecular(float R, float G, float B, float A);
void setAmbient(float R, float G, float B, float A);
/* the following function tell the OpenGL how to draw the
Texture of this kind of Material and is not important
here. */
void setMaterial();
};
void MaterialTexture::setDiffuse(float R, float G, float B, float A)
{
diffuse[0]=R; diffuse[1]=G; diffuse[2]=B; diffuse[3]=A;
}
// Create instances plastic and metal
MaterialTexture plastic;
plastic.setDiffuse(0.6, 0.0, 0.0, 1.0);
...
MaterialTexture metal;
metal.setDiffuse(...);
...
}; // end of namespace
因此,要创建一个类似红色塑料的球体,我只需要在显示回调中输入以下代码:
Material::MaterialTexture::plastic.setMaterial();
glutSolidSphere(...);
用g ++编译上面的代码,它给出了错误:
error: ‘plastic’ does not name a type
和
error: ‘metal’ does not name a type
在每个实例的三个设置函数(例如setDiffuse())的行中。
因此,它似乎不仅直接在命名空间中赋值,而且函数包含赋值是不允许的......我是对的吗?有没有其他方法来解决这个问题?或者,还有其他方法可以促进OpenGL编程吗?
答案 0 :(得分:0)
你根本无法“在不知名的地方”调用一个函数。这与名称空间无关。考虑这个完整的例子:
void f() { }
f();
int main()
{
}
它不会编译。
解决方案是添加三个不同的静态成员函数,返回三个特殊实例,实际上可以在另一个(私有)静态成员函数中创建。这是一个例子:
class MaterialTexture
{
private:
float diffuse[4];
float specular[4];
float ambient[4];
static MaterialTexture makePlastic()
{
MaterialTexture plastic;
plastic.setDiffuse(0.6, 0.0, 0.0, 1.0);
return plastic;
}
public:
// Three functions below set the values for data members above.
void setDiffuse(float R, float G, float B, float A);
void setSpecular(float R, float G, float B, float A);
void setAmbient(float R, float G, float B, float A);
static MaterialTexture &plastic()
{
static MaterialTexture plastic = makePlastic();
return plastic;
}
};
但是,还有改进的余地,特别是如果你可以使用C ++ 11:
std::array
。double
代替float
。以下是一个完整的改进示例:
class MaterialTexture
{
private:
std::array<double, 4> diffuse;
std::array<double, 4> specular;
std::array<double, 4> ambient;
MaterialTexture(std::array<double, 4> const &diffuse, std::array<double, 4> const &specular,
std::array<double, 4> const &ambient) : diffuse(diffuse), specular(specular), ambient(ambient) {}
public:
// Three functions below set the values for data members above.
void setDiffuse(double R, double G, double B, double A);
void setSpecular(double R, double G, double B, double A);
void setAmbient(double R, double G, double B, double A);
static MaterialTexture &plastic()
{
static MaterialTexture plastic(
{ 0.6, 0.0, 0.0, 1.0 },
{ 0.0, 0.0, 0.0, 0.0 },
{ 0.0, 0.0, 0.0, 0.0 }
);
return plastic;
}
};
答案 1 :(得分:0)
除了你的代码的直接问题(所有可执行代码必须在一个函数内,在你的例子中不是这种情况),如何最好地构造它是非常主观的。在此基础上,请阅读以下段落,并附上隐含的&#34;恕我直言&#34;。以下是我提出的一个简单的解决方案,可以帮助你。
我会坚持你的命名以保持一致。即使我不喜欢这个班级的名字MaterialTexture
。在OpenGL的上下文中,该名称表明该类封装了一个纹理,但它不是。
首先,类应该具有初始化所有成员的构造函数。这确保即使未按预期使用类,也不会有任何类成员处于未初始化状态,否则可能导致未定义的行为。我同意在这种情况下使用带有12个浮点参数的构造函数会很尴尬。我会有一个默认构造函数,将所有成员初始化为合理的默认值,例如匹配旧固定管道的默认值:
MaterialTexture::MaterialTexture()
{
diffuse[0] = 0.8f;
diffuse[1] = 0.8f;
diffuse[2] = 0.8f;
diffuse[3] = 1.0f;
ambient[0] = 0.2f;
ambient[1] = 0.2f;
ambient[2] = 0.2f;
ambient[3] = 1.0f;
specular[0] = 0.0f;
specular[1] = 0.0f;
specular[2] = 0.0f;
specular[3] = 1.0f;
}
您的setDiffuse()
,setSpecular()
和setAmbient()
方法看起来不错。
现在问题是您在哪里创建特定材料(plastic
,metal
)。在这个类中使用静态方法是一种选择,但我不认为它是一个干净的设计。此类表示通用材料。将特定材料的知识放在同一个类中会混淆应该分开的责任。
我会有一个单独的类,提供特定的材料。关于如何设计和结构化有很多选择。最简单的方法是创建特定材料的方法。对于允许您在不更改任何接口的情况下添加材料的更好的方法,您可以按名称引用它们:
class MaterialLibrary
{
public:
MaterialLibrary();
const MaterialTexture& getMaterial(const std::string& materialName) const;
private:
std::map<std::string, MaterialTexture> m_materials;
}
MaterialLibrary::MaterialLibrary()
{
MaterialTexture plastic;
plastic.setDiffuse(...);
...
m_materials.insert(std::make_pair("plastic", plastic));
MaterialTexture metal;
metal.setDiffuse(...);
...
m_materials.insert(std::make_pair("metal", metal));
}
const MaterialTexture& MaterialLibrary::getMaterial(const std::string& materialName) const
{
return m_materials.at(materialName);
}
您可以轻松更改此选项以从配置文件中读取材料列表,而不是在代码中将其包含在内。
现在您只需要创建并使用MaterialLibrary
的一个实例。同样,有多种方法可以做到这一点。你可以把它变成一个单身人士。或者在启动期间创建一个实例,并将该实例传递给需要它的每个人。
获得MaterialLibrary
实例lib
后,您现在可以获取材料:
const MaterialTexture& mat = lib.getMaterial("plastic");
你可以变得更加漂亮并使MaterialLibrary
更多的工厂类,例如模板工厂。这将进一步分离责任,MaterialLibrary
仅维护材料清单,并提供对它们的访问,但不知道如何构建可用的特定材料列表。您决定在抽象中走多远就是您的决定。