我正在制作一个基本的渲染引擎。
为了让渲染引擎对各种几何体进行操作, 我做了这个课:
class Geometry
{
protected:
ID3D10Buffer* m_pVertexBuffer;
ID3D10Buffer* m_pIndexBuffer;
public:
[...]
};
现在,我希望用户能够通过继承此类来创建自己的几何体。
所以让我们假设用户做了class Cube : public Geometry
用户必须在初始化时创建vertexbuffer和indexbuffer。
这是一个问题,因为每次创建一个新的Cube对象时它都会重新创建vertexbuffer和indexbuffer。每个派生类应该只有一个vertexbuffer和indexbuffer实例。无论是那种,还是完全不同的设计。
解决方案可能是为继承类创建单独的static ID3D10Buffer*
,并将继承类的指针设置为等于构造函数中的指针。
但这需要一个静态方法,如static void CreateBuffers()
,用户必须在他的应用程序中为他决定从Geometry
继承的每种类型显式调用一次。这似乎不是一个很好的设计。
这个问题的解决方案是什么?
答案 0 :(得分:4)
您应该将实例的概念与网格的概念分开。这意味着您为一个多维数据集创建一个Geometry版本,该多维数据集表示多维数据集的顶点和索引缓冲区。
然后,您将引入一个名为GeometryInstance的新类,其中包含一个转换矩阵。该类还应具有指向Geometry的指针/引用。现在,您可以通过创建几何所有引用相同Geometry对象的GeometryInstances来创建新几何实例,而不是复制内存或在创建新框时工作。
编辑: 假设你有问题中的Geometry类和评论中的Mesh类,你的Mesh类看起来应该是这样的:
class Mesh {
private:
Matrix4x4 transformation;
Geometry* geometry;
public:
Mesh(const Matrix4x4 _t, Geometry* _g) : transformation(_t), geometry(_g) {}
}
现在,在创建场景时,你想要做这样的事情
...
std::vector<Mesh> myMeshes;
// OrdinaryGeometry is a class inheriting Geometry
OrdinaryGeometry* geom = new OrdinaryGeometry(...);
for(int i = 0; i < ordinaryGeomCount; ++i) {
// generateTransform is a function that generates some
// transformation Matrix given an index, just as an example
myMeshes.push_back(Mesh(generateTransform(i), geom);
}
// SpecialGeometry is a class inheriting Geometry with a different
// set of vertices and indices
SuperSpecialGeometry* specialGeom = new SuperSpecialGeometry(...);
for(int i = 0; i < specialGeomCount; ++i) {
myMeshes.push_back(Mesh(generateTransform(i), specialGeom);
}
// Now render all instances
for(int i = 0; i < myMeshes.size(); ++i) {
render(myMeshes[i]);
}
请注意我们如何只有两个在多个网格之间共享的几何对象。理想情况下,这些应该使用std :: shared_ptr或类似的东西进行重新计算,但它超出了问题的范围。
答案 1 :(得分:2)
在立方体示例中对几何进行子分类有什么意义?立方体只是几何体的一个实例,它具有一组特定的三角形和索引。 Cube类和Sphere类之间没有区别,除了它们用不同的数据填充它们的三角形/索引缓冲区。所以数据本身就是重要的。您需要一种方法来允许用户为您的引擎提供各种形状数据,然后在创建之后以某种方式引用该数据。
要提供形状数据,您有两种选择。你可以决定保持Geometry的细节是私有的,并提供一些接口,它接受来自文件的字符串等原始数据,或者填充在某个用户自定义函数中的float数组,为该数据创建Geometry实例,然后给出用户对该实例的一些句柄(或允许用户指定句柄)。或者,你可以创建一些像GeometryInfo这样的类,它有用户填充他/她自己的方法addTriangle,addVertex等,然后有一些接受GeometryInfo的函数,为该数据创建一个Geometry实例,然后再给用户一些句柄。
在这两种情况下,你需要提供一些界面,允许用户说“这里是一些数据,从中取出一些东西并给它一些处理。最小化它会有我所描述的功能。你需要维护一个映射引擎中创建的几何实例的某处。这样就可以强制执行每个形状规则的一个实例,这样就可以将用户想要的内容(“球”,“立方体”)与引擎需要的内容相关联(带填充缓冲区的几何体) )。
现在关于句柄。我要么让用户将数据与名称相关联,比如“Ball”,要么返回一些用户随后会与某个“Ball”实例关联的整数。这样,当您制作Rocket类时,用户可以从您的引擎请求“Ball”实例,其他各种对象可以使用“Ball”,一切都很好,因为它们只是存储句柄,而不是球本身。我不建议存储指向实际Geometry实例的指针。网格物体不拥有几何体,因为它可以与其他网格物体共享。它不需要访问几何的成员,因为渲染器处理grunt工作。所以这是一种不必要的依赖。唯一的原因是速度,但使用散列处理你的句柄也会同样好。
现在举一些例子:
提供形状数据:
//option one
engine->CreateGeometryFromFile("ball.txt", "Ball");
//option two
GeometryInfo ball;
ball.addTriangle(0, 1, 0, 1);
ball.addTriangle(...);
...
engine->CreateGeometryFromInfo(ball, "Ball");
使用句柄引用该数据:
class Drawable
{
std::string shape;
Matrix transform;
};
class Rocket : public Drawable
{
Rocket() { shape = "Ball";}
//other stuff here for physics maybe
};
class BallShapedEnemy : public Drawable
{
BallShapedEnemy() { shape = "Ball";}
...
}
...
...in user's render loop...
for each (drawable in myDrawables)
{
engine->Render(drawable.GetShape(), drawable.GetTransform());
}
现在,为每个不同的游戏对象(例如Rocket)设置一个单独的类是有争议的,并且完全是另一个问题的主题,我只是从评论中看起来像你的例子。
答案 2 :(得分:1)
这可能是一种草率的方式,但你不能只做一个单身人士吗?
#pragma once
#include <iostream>
#define GEOM Geometry::getInstance()
class Geometry
{
protected:
static Geometry* ptrInstance;
static Geometry* getInstance();
float* m_pVertexBuffer;
float* m_pIndexBuffer;
public:
Geometry(void);
~Geometry(void);
void callGeom();
};
#include "Geometry.h"
Geometry* Geometry::ptrInstance = 0;
Geometry::Geometry(void)
{
}
Geometry::~Geometry(void)
{
}
Geometry* Geometry::getInstance()
{
if(ptrInstance == 0)
{
ptrInstance = new Geometry();
}
return ptrInstance;
}
void Geometry::callGeom()
{
std::cout << "Call successful!" << std::endl;
}
这种方法唯一的问题是你只有一个Geometry对象而我假设你可能需要多个?如果不是它可能有用,但我认为Lasserallan的方法可能是一个更好的实现你想要的。