Was ich haben will ist eine wirklich flexible Möglichkeit Vertexbuffer mit den "nötigen" Eigenschaften zu erstellen. Ich bin mir auch nicht sicher ob das geht. Vielleicht gibt es hier den einen oder anderen der einen Weg gefunden hat und sein Wissen teilen möchte.
Du könntest deine Vertexdaten z.B. so gruppieren, dass die die Möglichkeit hast Streams zu deaktivieren, falls nicht gebraucht (z.B. Normal/Tangent für Shadowmapping o.Ä.). Beispielsweise könntest du Position und Texcoord0 in einen Vertexpuffer packen. Zusätzliche Streams wären dann z.B. Texturkoordinaten, Skinningdaten, Tangentframe etc... Die Vertexdeklaration (bzw FVF) ist dann abhängig davon welche Attribute/Streams du für einen Pass benötigst. Die Auswahl könntest du automatisieren, wenn dir die Informationen vorliegen wie das Inputlayout von deinem Vertexshader aussieht. Dann kannst du ggf auch fehlende Vertexattribute dazupatchen oder was auch immer notwendig ist.
Hier ggf als Gedankenstütze wie ich das in meinem kleinen Testframework gelöst habe:
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
struct VertexStream
{
VertexBuffer* vertexBuffer;
uint32 stride;
};
struct VertexElement
{
VertexElement(uint32 streamIndex, const String& semantic, uint32 index, eVertexElementType type)
: streamIndex(streamIndex)
, semantic(semantic)
, index(index)
, type(type)
{
}
uint32 streamIndex;
String semantic;
uint32 index;
eVertexElementType type;
};
|
VertexElement beschreibt genau ein Attribute und
VertexStream hält alle Daten vor die notwendig für einen Drawcall sind.
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
|
class VertexDeclaration
{
public:
static VertexDeclaration* create(const std::vector<VertexElement>& elementList);
const std::vector<D3D11_INPUT_ELEMENT_DESC>& getElements() const { return m_VertexElements; }
// [...]
private:
std::vector<D3D11_INPUT_ELEMENT_DESC> m_VertexElements;
};
|
VertexDeclaration ist die Beschreibung von den Vertexdaten. Also in diesem Fall nur ein sehr dünner Layer über der Schnittstelle die DX11 anbietet. Das könnte natürlich auch entsprechend abstrahiert werden um andere APIs zu unterstützen.
VertexAccess ist eine dünne Schicht die verwendet wird um Vertexstreams zusammen zu bauen und an den Renderkontext zu geben. Vertexdaten haben i.A. die üblichen Vertexpuffer/Indexpuffer und ein
VertexAccess Objekt das den Vertexzugriff innerhalb der Puffer beschreibt.
Verwendet wird das so:
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
void MeshChunk::initialize(const struct RawMesh& mesh, uint32 startIndex, uint32 indexCount)
{
const bool hasNormals = mesh.Normals.size() > 0;
const bool hasTangents = mesh.Tangents.size() > 0;
const bool hasTexoord[4] =
{
mesh.Texcoords0.size() > 0,
mesh.Texcoords1.size() > 0,
mesh.Texcoords2.size() > 0,
mesh.Texcoords3.size() > 0
};
uint32 vertexStride = sizeof(float) * 3;
if (hasNormals)
vertexStride += sizeof(float) * 3;
if (hasTangents)
vertexStride += sizeof(float) * 4;
for (uint32 i = 0; i < 4; ++i)
{
if (hasTexoord[i])
vertexStride += sizeof(float) * 2;
}
// create vertex accessor
std::vector<VertexElement> vertexElements;
vertexElements.push_back(m_RenderData->m_VertexAccess.addVertexElement(&m_RenderData->m_VertexBuffer, vertexStride, VET_FLOAT3, "POSITION", 0));
if (hasNormals)
{
vertexElements.push_back(m_RenderData->m_VertexAccess.addVertexElement(&m_RenderData->m_VertexBuffer, vertexStride, VET_FLOAT3, "NORMAL", 0));
}
if (hasTangents)
{
vertexElements.push_back(m_RenderData->m_VertexAccess.addVertexElement(&m_RenderData->m_VertexBuffer, vertexStride, VET_FLOAT4, "TANGENT", 0));
}
for (uint32 i = 0; i < 4; ++i)
{
if (hasTexoord[i])
{
vertexElements.push_back(m_RenderData->m_VertexAccess.addVertexElement(&m_RenderData->m_VertexBuffer, vertexStride, VET_FLOAT2, "TEXCOORD", i));
}
}
m_RenderData->m_VertexAccess.initDeclaration(vertexElements);
// [erzeugen der vertex-/indexpuffer]
}
|
Hoffe du kannst dem ein paar Informationen entnehmen.