DirectXMesh/Utilities/FlexibleVertexFormat.h

1063 строки
33 KiB
C++

//--------------------------------------------------------------------------------------
// File: FlexibleVertexFormat.h
//
// Helpers for legacy Direct3D 9 era FVF codes and vertex decls
//
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
//
// http://go.microsoft.com/fwlink/?LinkID=324981
//--------------------------------------------------------------------------------------
#pragma once
#include <d3d9.h>
#include <cstdint>
#include <iterator>
#include <vector>
namespace FVF
{
constexpr uint8_t g_declTypeSizes[] =
{
4, // D3DDECLTYPE_FLOAT1
8, // D3DDECLTYPE_FLOAT2
12, // D3DDECLTYPE_FLOAT3
16, // D3DDECLTYPE_FLOAT4
4, // D3DDECLTYPE_D3DCOLOR
4, // D3DDECLTYPE_UBYTE4
4, // D3DDECLTYPE_SHORT2
8, // D3DDECLTYPE_SHORT4
4, // D3DDECLTYPE_UBYTE4N
4, // D3DDECLTYPE_SHORT2N
8, // D3DDECLTYPE_SHORT4N
4, // D3DDECLTYPE_USHORT2N
8, // D3DDECLTYPE_USHORT4N
4, // D3DDECLTYPE_UDEC3
4, // D3DDECLTYPE_DEC3N
4, // D3DDECLTYPE_FLOAT16_2
8, // D3DDECLTYPE_FLOAT16_4
};
static_assert(std::size(g_declTypeSizes) == D3DDECLTYPE_UNUSED, "Mismatch of array size");
inline size_t ComputeVertexSize(uint32_t fvfCode)
{
if ((fvfCode & ((D3DFVF_RESERVED0 | D3DFVF_RESERVED2) & ~D3DFVF_POSITION_MASK)) != 0)
return 0;
const size_t numCoords = (fvfCode & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
if (numCoords > 8)
return 0;
size_t vertexSize = 0;
switch (fvfCode & D3DFVF_POSITION_MASK)
{
case 0: break;
case D3DFVF_XYZ: vertexSize = 3 * sizeof(float); break;
case D3DFVF_XYZRHW:
case D3DFVF_XYZB1:
case D3DFVF_XYZW:
vertexSize = 4 * sizeof(float);
break;
case D3DFVF_XYZB2: vertexSize = 5 * sizeof(float); break;
case D3DFVF_XYZB3: vertexSize = 6 * sizeof(float); break;
case D3DFVF_XYZB4: vertexSize = 7 * sizeof(float); break;
case D3DFVF_XYZB5: vertexSize = 8 * sizeof(float); break;
default: return 0;
}
if (fvfCode & D3DFVF_NORMAL)
vertexSize += 3 * sizeof(float);
if (fvfCode & D3DFVF_PSIZE)
vertexSize += sizeof(uint32_t);
if (fvfCode & D3DFVF_DIFFUSE)
vertexSize += sizeof(uint32_t);
if (fvfCode & D3DFVF_SPECULAR)
vertexSize += sizeof(uint32_t);
// Texture coordinates
uint32_t textureFormats = fvfCode >> 16u;
if (textureFormats)
{
for (size_t i = 0; i < numCoords; i++)
{
switch (textureFormats & 3)
{
case 0: vertexSize += 2 * sizeof(float); break;
case 1: vertexSize += 3 * sizeof(float); break;
case 2: vertexSize += 4 * sizeof(float); break;
case 3: vertexSize += 1 * sizeof(float); break;
}
textureFormats >>= 2;
}
}
else
{
vertexSize += numCoords * (2 * sizeof(float));
}
return vertexSize;
}
inline size_t ComputeVertexSize(const D3DVERTEXELEMENT9* pDecl, uint32_t stream)
{
if (!pDecl || stream >= 16u /*D3D10_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT*/)
return 0;
size_t currentSize = 0;
size_t count = 0;
//search for the max offset in the stream,
//(min)vertex size = max offset + type size
while (pDecl->Stream != 0xFF)
{
++count;
if (count > MAXD3DDECLLENGTH)
return 0;
// only look at items of this stream and vertex elements actually in the data stream (not generated)
// UV is phantom data.
if ((pDecl->Stream == stream) && (pDecl->Method != D3DDECLMETHOD_UV))
{
if (pDecl->Type >= std::size(g_declTypeSizes))
return 0;
const size_t slotSize = g_declTypeSizes[pDecl->Type];
if (currentSize < slotSize + pDecl->Offset)
currentSize = slotSize + pDecl->Offset;
}
++pDecl;
}
return currentSize;
}
// More secure version
inline size_t ComputeVertexSize(
_In_reads_(maxDeclLength) const D3DVERTEXELEMENT9* pDecl, size_t maxDeclLength, uint32_t stream)
{
if (!pDecl || stream >= 16u /*D3D10_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT*/)
return 0;
if (maxDeclLength > MAXD3DDECLLENGTH + 1)
return 0;
size_t currentSize = 0;
size_t count = 0;
//search for the max offset in the stream,
//(min)vertex size = max offset + type size
while (pDecl->Stream != 0xFF)
{
++count;
if (count > maxDeclLength)
return 0;
// only look at items of this stream and vertex elements actually in the data stream (not generated)
// UV is phantom data.
if ((pDecl->Stream == stream) && (pDecl->Method != D3DDECLMETHOD_UV))
{
if (pDecl->Type >= std::size(g_declTypeSizes))
return 0;
const size_t slotSize = g_declTypeSizes[pDecl->Type];
if (currentSize < slotSize + pDecl->Offset)
currentSize = slotSize + pDecl->Offset;
}
++pDecl;
}
return currentSize;
}
inline size_t GetDeclLength(const D3DVERTEXELEMENT9* pDecl)
{
if (!pDecl)
return 0;
size_t length = 0;
while (pDecl->Stream != 0xFF)
{
if (length >= MAXD3DDECLLENGTH)
return 0;
++pDecl;
++length;
}
return length;
}
_Success_(return)
inline bool CreateDeclFromFVF(uint32_t fvfCode, std::vector<D3DVERTEXELEMENT9>& decl)
{
static constexpr size_t s_texCoordSizes[] =
{
2 * sizeof(float),
3 * sizeof(float),
4 * sizeof(float),
sizeof(float)
};
decl.clear();
if ((fvfCode & ((D3DFVF_RESERVED0 | D3DFVF_RESERVED2) & ~D3DFVF_POSITION_MASK)) != 0)
return false;
const uint32_t nTexCoords = (fvfCode & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
if (nTexCoords > 8)
return false;
size_t offset = 0;
switch (fvfCode & D3DFVF_POSITION_MASK)
{
case 0:
break;
case D3DFVF_XYZRHW:
decl.emplace_back(
D3DVERTEXELEMENT9{ 0, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITIONT, 0 }
);
offset = sizeof(float) * 4;
break;
case D3DFVF_XYZW:
decl.emplace_back(
D3DVERTEXELEMENT9{ 0, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 }
);
offset = sizeof(float) * 4;
break;
default:
decl.emplace_back(
D3DVERTEXELEMENT9{ 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 }
);
offset = sizeof(float) * 3;
break;
}
size_t weights = 0;
switch (fvfCode & D3DFVF_POSITION_MASK)
{
case D3DFVF_XYZB1: weights = 1; break;
case D3DFVF_XYZB2: weights = 2; break;
case D3DFVF_XYZB3: weights = 3; break;
case D3DFVF_XYZB4: weights = 4; break;
case D3DFVF_XYZB5: weights = 5; break;
}
if (weights > 0)
{
if (fvfCode & (D3DFVF_LASTBETA_UBYTE4 | D3DFVF_LASTBETA_D3DCOLOR))
{
// subtract one to convert to D3DDECLTYPE_FLOAT* and another for where the indices were
if (weights > 1)
{
decl.emplace_back(
D3DVERTEXELEMENT9{ 0, static_cast<uint16_t>(offset), static_cast<uint8_t>(weights - 2),
D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDWEIGHT, 0 }
);
offset += sizeof(float) * (weights - 1);
}
decl.emplace_back(
D3DVERTEXELEMENT9{ 0, static_cast<uint16_t>(offset),
static_cast<uint8_t>((fvfCode & D3DFVF_LASTBETA_UBYTE4) ? D3DDECLTYPE_UBYTE4 : D3DDECLTYPE_D3DCOLOR),
D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDINDICES, 0 }
);
offset += sizeof(uint32_t);
}
else if (weights == 5)
{
// D3DFVF_XYZB5 is only supported when the 5th beta is D3DFVF_LASTBETA_UBYTE4/D3DCOLOR
decl.clear();
return false;
}
else
{
// subtract one to convert to D3DDECLTYPE_FLOAT*
decl.emplace_back(
D3DVERTEXELEMENT9{ 0, static_cast<uint16_t>(offset), static_cast<uint8_t>(weights - 1),
D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDWEIGHT, 0 }
);
offset += sizeof(float) * (weights - 1);
}
}
if (fvfCode & D3DFVF_NORMAL)
{
decl.emplace_back(
D3DVERTEXELEMENT9{ 0, static_cast<uint16_t>(offset), D3DDECLTYPE_FLOAT3,
D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0 }
);
offset += sizeof(float) * 3;
}
if (fvfCode & D3DFVF_PSIZE)
{
decl.emplace_back(
D3DVERTEXELEMENT9{ 0, static_cast<uint16_t>(offset), D3DDECLTYPE_FLOAT1,
D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_PSIZE, 0 }
);
offset += sizeof(float);
}
if (fvfCode & D3DFVF_DIFFUSE)
{
decl.emplace_back(
D3DVERTEXELEMENT9{ 0, static_cast<uint16_t>(offset), D3DDECLTYPE_D3DCOLOR,
D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0 }
);
offset += sizeof(uint32_t);
}
if (fvfCode & D3DFVF_SPECULAR)
{
decl.emplace_back(
D3DVERTEXELEMENT9{ 0, static_cast<uint16_t>(offset), D3DDECLTYPE_D3DCOLOR,
D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 1 }
);
offset += sizeof(uint32_t);
}
if (nTexCoords > 0)
{
for (uint32_t t = 0; t < nTexCoords; ++t)
{
const size_t texCoordSize = s_texCoordSizes[(fvfCode >> (16 + t * 2)) & 0x3];
// D3DDECLTYPE_FLOAT1 = 0, D3DDECLTYPE_FLOAT4 = 3
decl.emplace_back(
D3DVERTEXELEMENT9{ 0, static_cast<uint16_t>(offset),
static_cast<uint8_t>(texCoordSize / sizeof(float) - 1),
D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, static_cast<uint8_t>(t) }
);
offset += texCoordSize;
}
}
decl.emplace_back(D3DVERTEXELEMENT9{ 0xFF, 0, D3DDECLTYPE_UNUSED, 0, 0, 0 });
return true;
}
#ifdef __d3d11_h__
_Success_(return)
inline bool CreateInputLayoutFromFVF(uint32_t fvfCode, std::vector<D3D11_INPUT_ELEMENT_DESC>& il)
{
static constexpr DXGI_FORMAT s_blendFormats[] =
{
DXGI_FORMAT_R32_FLOAT,
DXGI_FORMAT_R32G32_FLOAT,
DXGI_FORMAT_R32G32B32_FLOAT,
DXGI_FORMAT_R32G32B32A32_FLOAT,
};
static constexpr DXGI_FORMAT s_texCoordFormats[] =
{
DXGI_FORMAT_R32G32_FLOAT,
DXGI_FORMAT_R32G32B32_FLOAT,
DXGI_FORMAT_R32G32B32A32_FLOAT,
DXGI_FORMAT_R32_FLOAT
};
il.clear();
if ((fvfCode & ((D3DFVF_RESERVED0 | D3DFVF_RESERVED2) & ~D3DFVF_POSITION_MASK)) != 0)
return false;
const uint32_t nTexCoords = (fvfCode & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
if (nTexCoords > 8)
return false;
switch (fvfCode & D3DFVF_POSITION_MASK)
{
case 0:
break;
case D3DFVF_XYZRHW:
case D3DFVF_XYZW:
il.emplace_back(
D3D11_INPUT_ELEMENT_DESC{ "SV_Position", 0, DXGI_FORMAT_R32G32B32A32_FLOAT,
0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }
);
break;
default:
il.emplace_back(
D3D11_INPUT_ELEMENT_DESC{ "SV_Position", 0, DXGI_FORMAT_R32G32B32_FLOAT,
0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }
);
break;
}
size_t weights = 0;
switch (fvfCode & D3DFVF_POSITION_MASK)
{
case D3DFVF_XYZB1: weights = 1; break;
case D3DFVF_XYZB2: weights = 2; break;
case D3DFVF_XYZB3: weights = 3; break;
case D3DFVF_XYZB4: weights = 4; break;
case D3DFVF_XYZB5: weights = 5; break;
}
if (weights > 0)
{
if (fvfCode & (D3DFVF_LASTBETA_UBYTE4 | D3DFVF_LASTBETA_D3DCOLOR))
{
// subtract one for where the blendindices were
if (weights > 1)
{
il.emplace_back(
D3D11_INPUT_ELEMENT_DESC{ "BLENDWEIGHT", 0, s_blendFormats[weights - 2],
0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }
);
}
il.emplace_back(
D3D11_INPUT_ELEMENT_DESC{ "BLENDINDICES", 0,
(fvfCode & D3DFVF_LASTBETA_UBYTE4) ? DXGI_FORMAT_R8G8B8A8_UNORM : DXGI_FORMAT_B8G8R8A8_UNORM,
0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }
);
}
else if (weights == 5)
{
// D3DFVF_XYZB5 is only supported when the 5th beta is D3DFVF_LASTBETA_UBYTE4/D3DCOLOR
il.clear();
return false;
}
else
{
il.emplace_back(
D3D11_INPUT_ELEMENT_DESC{ "BLENDWEIGHT", 0, s_blendFormats[weights - 1],
0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }
);
}
}
if (fvfCode & D3DFVF_NORMAL)
{
il.emplace_back(
D3D11_INPUT_ELEMENT_DESC{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT,
0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }
);
}
if (fvfCode & D3DFVF_PSIZE)
{
il.emplace_back(
D3D11_INPUT_ELEMENT_DESC{ "PSIZE", 0, DXGI_FORMAT_R32_FLOAT,
0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }
);
}
if (fvfCode & D3DFVF_DIFFUSE)
{
il.emplace_back(
D3D11_INPUT_ELEMENT_DESC{ "COLOR", 0, DXGI_FORMAT_B8G8R8A8_UNORM,
0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }
);
}
if (fvfCode & D3DFVF_SPECULAR)
{
il.emplace_back(
D3D11_INPUT_ELEMENT_DESC{ "COLOR", 1, DXGI_FORMAT_B8G8R8A8_UNORM,
0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }
);
}
if (nTexCoords > 0)
{
for (uint32_t t = 0; t < nTexCoords; ++t)
{
const size_t index = (fvfCode >> (16 + t * 2)) & 0x3;
il.emplace_back(
D3D11_INPUT_ELEMENT_DESC{ "TEXCOORD", static_cast<UINT>(t),
s_texCoordFormats[index],
0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }
);
}
}
return true;
}
#endif // __d3d11_h__
#ifdef __d3d12_h__
_Success_(return)
inline bool CreateInputLayoutFromFVF(uint32_t fvfCode, std::vector<D3D12_INPUT_ELEMENT_DESC>& il)
{
static constexpr DXGI_FORMAT s_blendFormats[] =
{
DXGI_FORMAT_R32_FLOAT,
DXGI_FORMAT_R32G32_FLOAT,
DXGI_FORMAT_R32G32B32_FLOAT,
DXGI_FORMAT_R32G32B32A32_FLOAT,
};
static constexpr DXGI_FORMAT s_texCoordFormats[] =
{
DXGI_FORMAT_R32G32_FLOAT,
DXGI_FORMAT_R32G32B32_FLOAT,
DXGI_FORMAT_R32G32B32A32_FLOAT,
DXGI_FORMAT_R32_FLOAT
};
il.clear();
if ((fvfCode & ((D3DFVF_RESERVED0 | D3DFVF_RESERVED2) & ~D3DFVF_POSITION_MASK)) != 0)
return false;
uint32_t nTexCoords = (fvfCode & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
if (nTexCoords > 8)
return false;
switch (fvfCode & D3DFVF_POSITION_MASK)
{
case 0:
break;
case D3DFVF_XYZRHW:
case D3DFVF_XYZW:
il.emplace_back(
D3D12_INPUT_ELEMENT_DESC{ "SV_Position", 0, DXGI_FORMAT_R32G32B32A32_FLOAT,
0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
);
break;
default:
il.emplace_back(
D3D12_INPUT_ELEMENT_DESC{ "SV_Position", 0, DXGI_FORMAT_R32G32B32_FLOAT,
0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
);
break;
}
size_t weights = 0;
switch (fvfCode & D3DFVF_POSITION_MASK)
{
case D3DFVF_XYZB1: weights = 1; break;
case D3DFVF_XYZB2: weights = 2; break;
case D3DFVF_XYZB3: weights = 3; break;
case D3DFVF_XYZB4: weights = 4; break;
case D3DFVF_XYZB5: weights = 5; break;
}
if (weights > 0)
{
if (fvfCode & (D3DFVF_LASTBETA_UBYTE4 | D3DFVF_LASTBETA_D3DCOLOR))
{
// subtract one for where the blendindices were
if (weights > 1)
{
il.emplace_back(
D3D12_INPUT_ELEMENT_DESC{ "BLENDWEIGHT", 0, s_blendFormats[weights - 2],
0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
);
}
il.emplace_back(
D3D12_INPUT_ELEMENT_DESC{ "BLENDINDICES", 0,
(fvfCode & D3DFVF_LASTBETA_UBYTE4) ? DXGI_FORMAT_R8G8B8A8_UNORM : DXGI_FORMAT_B8G8R8A8_UNORM,
0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
);
}
else if (weights == 5)
{
// D3DFVF_XYZB5 is only supported when the 5th beta is D3DFVF_LASTBETA_UBYTE4/D3DCOLOR
il.clear();
return false;
}
else
{
il.emplace_back(
D3D12_INPUT_ELEMENT_DESC{ "BLENDWEIGHT", 0, s_blendFormats[weights - 1],
0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
);
}
}
if (fvfCode & D3DFVF_NORMAL)
{
il.emplace_back(
D3D12_INPUT_ELEMENT_DESC{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT,
0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
);
}
if (fvfCode & D3DFVF_PSIZE)
{
il.emplace_back(
D3D12_INPUT_ELEMENT_DESC{ "PSIZE", 0, DXGI_FORMAT_R32_FLOAT,
0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
);
}
if (fvfCode & D3DFVF_DIFFUSE)
{
il.emplace_back(
D3D12_INPUT_ELEMENT_DESC{ "COLOR", 0, DXGI_FORMAT_B8G8R8A8_UNORM,
0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
);
}
if (fvfCode & D3DFVF_SPECULAR)
{
il.emplace_back(
D3D12_INPUT_ELEMENT_DESC{ "COLOR", 1, DXGI_FORMAT_B8G8R8A8_UNORM,
0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
);
}
if (nTexCoords > 0)
{
for (uint32_t t = 0; t < nTexCoords; ++t)
{
size_t index = (fvfCode >> (16 + t * 2)) & 0x3;
il.emplace_back(
D3D12_INPUT_ELEMENT_DESC{ "TEXCOORD", static_cast<UINT>(t),
s_texCoordFormats[index],
0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
);
}
}
return true;
}
#endif // __d3d12_h__
inline uint32_t ComputeFVF(const D3DVERTEXELEMENT9* pDecl)
{
if (!pDecl)
return 0;
// validate vertex declaration
auto pCurrent = pDecl;
size_t count = 0;
size_t offset = 0;
while (pCurrent->Stream != 0xFF)
{
++count;
if (count > MAXD3DDECLLENGTH)
return 0;
if (pCurrent->Stream != 0)
return 0;
if (pCurrent->Type > D3DDECLTYPE_SHORT4)
return 0;
if (offset != pCurrent->Offset)
return 0;
if (pCurrent->Method > D3DDECLMETHOD_LOOKUP)
return 0;
if (((pCurrent->Usage > D3DDECLUSAGE_TEXCOORD)
&& (pCurrent->Usage != D3DDECLUSAGE_POSITIONT)
&& (pCurrent->Usage != D3DDECLUSAGE_COLOR)))
{
return 0;
}
if ((pCurrent->Usage == D3DDECLUSAGE_COLOR) && (pCurrent->UsageIndex > 1))
{
return 0;
}
offset += g_declTypeSizes[pCurrent->Type];
++pCurrent;
}
// Build FVF code
pCurrent = pDecl;
uint32_t fvfCode = 0;
if (pCurrent->Usage == D3DDECLUSAGE_POSITION)
{
if (pCurrent->Type == D3DDECLTYPE_FLOAT3)
{
size_t weights = 0;
++pCurrent;
if (pCurrent->Usage == D3DDECLUSAGE_BLENDWEIGHT)
{
if ((pCurrent->Type >= D3DDECLTYPE_FLOAT1) && (pCurrent->Type <= D3DDECLTYPE_FLOAT4))
{
weights = pCurrent->Type - D3DDECLTYPE_FLOAT1 + 1;
++pCurrent;
}
else
{
return 0;
}
}
if (pCurrent->Usage == D3DDECLUSAGE_BLENDINDICES)
{
if (pCurrent->Type == D3DDECLTYPE_UBYTE4)
{
fvfCode |= D3DFVF_LASTBETA_UBYTE4;
++weights;
++pCurrent;
}
else if (pCurrent->Type == D3DDECLTYPE_D3DCOLOR)
{
fvfCode |= D3DFVF_LASTBETA_D3DCOLOR;
++weights;
++pCurrent;
}
else
{
return 0;
}
}
switch (weights)
{
case 0: fvfCode |= D3DFVF_XYZ; break;
case 1: fvfCode |= D3DFVF_XYZB1; break;
case 2: fvfCode |= D3DFVF_XYZB2; break;
case 3: fvfCode |= D3DFVF_XYZB3; break;
case 4: fvfCode |= D3DFVF_XYZB4; break;
case 5: fvfCode |= D3DFVF_XYZB5; break;
}
}
else if (pCurrent->Type == D3DDECLTYPE_FLOAT4)
{
fvfCode |= D3DFVF_XYZW;
++pCurrent;
}
}
else if ((pCurrent->Usage == D3DDECLUSAGE_POSITIONT)
&& (pCurrent->Type == D3DDECLTYPE_FLOAT4))
{
fvfCode |= D3DFVF_XYZRHW;
++pCurrent;
}
// Normal
if ((pCurrent->Usage == D3DDECLUSAGE_NORMAL)
&& (pCurrent->Type == D3DDECLTYPE_FLOAT3))
{
fvfCode |= D3DFVF_NORMAL;
++pCurrent;
}
// Point size
if ((pCurrent->Usage == D3DDECLUSAGE_PSIZE)
&& (pCurrent->Type == D3DDECLTYPE_FLOAT1))
{
fvfCode |= D3DFVF_PSIZE;
++pCurrent;
}
// Diffuse
if ((pCurrent->Usage == D3DDECLUSAGE_COLOR)
&& (pCurrent->UsageIndex == 0)
&& (pCurrent->Type == D3DDECLTYPE_D3DCOLOR))
{
fvfCode |= D3DFVF_DIFFUSE;
++pCurrent;
}
// Specular
if ((pCurrent->Usage == D3DDECLUSAGE_COLOR)
&& (pCurrent->UsageIndex == 1)
&& (pCurrent->Type == D3DDECLTYPE_D3DCOLOR))
{
fvfCode |= D3DFVF_SPECULAR;
++pCurrent;
}
// Texture coordinates
size_t i;
for (i = 0; i < 8; ++i)
{
if ((pCurrent->Usage == D3DDECLUSAGE_TEXCOORD)
&& (pCurrent->Type == D3DDECLTYPE_FLOAT1)
&& (pCurrent->UsageIndex == i))
{
fvfCode |= static_cast<uint32_t>(D3DFVF_TEXCOORDSIZE1(i));
}
else if ((pCurrent->Usage == D3DDECLUSAGE_TEXCOORD)
&& (pCurrent->Type == D3DDECLTYPE_FLOAT2)
&& (pCurrent->UsageIndex == i))
{
fvfCode |= static_cast<uint32_t>(D3DFVF_TEXCOORDSIZE2(i));
}
else if ((pCurrent->Usage == D3DDECLUSAGE_TEXCOORD)
&& (pCurrent->Type == D3DDECLTYPE_FLOAT3)
&& (pCurrent->UsageIndex == i))
{
fvfCode |= static_cast<uint32_t>(D3DFVF_TEXCOORDSIZE3(i));
}
else if ((pCurrent->Usage == D3DDECLUSAGE_TEXCOORD)
&& (pCurrent->Type == D3DDECLTYPE_FLOAT4)
&& (pCurrent->UsageIndex == i))
{
fvfCode |= static_cast<uint32_t>(D3DFVF_TEXCOORDSIZE4(i));
}
else
break;
++pCurrent;
}
fvfCode |= static_cast<uint32_t>(i << D3DFVF_TEXCOUNT_SHIFT);
if (pCurrent->Stream != 0xff)
{
return 0;
}
return fvfCode;
}
// More secure version
inline uint32_t ComputeFVF(
_In_reads_(maxDeclLength) const D3DVERTEXELEMENT9* pDecl, size_t maxDeclLength)
{
if (!pDecl)
return 0;
if (maxDeclLength > MAXD3DDECLLENGTH + 1)
return 0;
// validate vertex declaration
auto pCurrent = pDecl;
size_t count = 0;
size_t offset = 0;
while (pCurrent->Stream != 0xFF)
{
++count;
if (count > maxDeclLength)
return 0;
if (pCurrent->Stream != 0)
return 0;
if (pCurrent->Type > D3DDECLTYPE_SHORT4)
return 0;
if (offset != pCurrent->Offset)
return 0;
if (pCurrent->Method > D3DDECLMETHOD_LOOKUP)
return 0;
if (((pCurrent->Usage > D3DDECLUSAGE_TEXCOORD)
&& (pCurrent->Usage != D3DDECLUSAGE_POSITIONT)
&& (pCurrent->Usage != D3DDECLUSAGE_COLOR)))
{
return 0;
}
if ((pCurrent->Usage == D3DDECLUSAGE_COLOR) && (pCurrent->UsageIndex > 1))
{
return 0;
}
offset += g_declTypeSizes[pCurrent->Type];
++pCurrent;
}
// Build FVF code
pCurrent = pDecl;
count = 0;
uint32_t fvfCode = 0;
if (pCurrent->Usage == D3DDECLUSAGE_POSITION)
{
if (pCurrent->Type == D3DDECLTYPE_FLOAT3)
{
size_t weights = 0;
++count;
if (count > maxDeclLength)
return 0;
++pCurrent;
if (pCurrent->Usage == D3DDECLUSAGE_BLENDWEIGHT)
{
if ((pCurrent->Type >= D3DDECLTYPE_FLOAT1) && (pCurrent->Type <= D3DDECLTYPE_FLOAT4))
{
weights = pCurrent->Type - D3DDECLTYPE_FLOAT1 + 1;
++count;
if (count > maxDeclLength)
return 0;
++pCurrent;
}
else
{
return 0;
}
}
if (pCurrent->Usage == D3DDECLUSAGE_BLENDINDICES)
{
if (pCurrent->Type == D3DDECLTYPE_UBYTE4)
{
fvfCode |= D3DFVF_LASTBETA_UBYTE4;
++weights;
++count;
if (count > maxDeclLength)
return 0;
++pCurrent;
}
else if (pCurrent->Type == D3DDECLTYPE_D3DCOLOR)
{
fvfCode |= D3DFVF_LASTBETA_D3DCOLOR;
++weights;
++count;
if (count > maxDeclLength)
return 0;
++pCurrent;
}
else
{
return 0;
}
}
switch (weights)
{
case 0: fvfCode |= D3DFVF_XYZ; break;
case 1: fvfCode |= D3DFVF_XYZB1; break;
case 2: fvfCode |= D3DFVF_XYZB2; break;
case 3: fvfCode |= D3DFVF_XYZB3; break;
case 4: fvfCode |= D3DFVF_XYZB4; break;
case 5: fvfCode |= D3DFVF_XYZB5; break;
}
}
else if (pCurrent->Type == D3DDECLTYPE_FLOAT4)
{
fvfCode |= D3DFVF_XYZW;
++count;
if (count > maxDeclLength)
return 0;
++pCurrent;
}
}
else if ((pCurrent->Usage == D3DDECLUSAGE_POSITIONT)
&& (pCurrent->Type == D3DDECLTYPE_FLOAT4))
{
fvfCode |= D3DFVF_XYZRHW;
++count;
if (count > maxDeclLength)
return 0;
++pCurrent;
}
// Normal
if ((pCurrent->Usage == D3DDECLUSAGE_NORMAL)
&& (pCurrent->Type == D3DDECLTYPE_FLOAT3))
{
fvfCode |= D3DFVF_NORMAL;
++count;
if (count > maxDeclLength)
return 0;
++pCurrent;
}
// Point size
if ((pCurrent->Usage == D3DDECLUSAGE_PSIZE)
&& (pCurrent->Type == D3DDECLTYPE_FLOAT1))
{
fvfCode |= D3DFVF_PSIZE;
++count;
if (count > maxDeclLength)
return 0;
++pCurrent;
}
// Diffuse
if ((pCurrent->Usage == D3DDECLUSAGE_COLOR)
&& (pCurrent->UsageIndex == 0)
&& (pCurrent->Type == D3DDECLTYPE_D3DCOLOR))
{
fvfCode |= D3DFVF_DIFFUSE;
++count;
if (count > maxDeclLength)
return 0;
++pCurrent;
}
// Specular
if ((pCurrent->Usage == D3DDECLUSAGE_COLOR)
&& (pCurrent->UsageIndex == 1)
&& (pCurrent->Type == D3DDECLTYPE_D3DCOLOR))
{
fvfCode |= D3DFVF_SPECULAR;
++count;
if (count > maxDeclLength)
return 0;
++pCurrent;
}
// Texture coordinates
size_t i;
for (i = 0; i < 8; ++i)
{
if ((pCurrent->Usage == D3DDECLUSAGE_TEXCOORD)
&& (pCurrent->Type == D3DDECLTYPE_FLOAT1)
&& (pCurrent->UsageIndex == i))
{
fvfCode |= static_cast<uint32_t>(D3DFVF_TEXCOORDSIZE1(i));
}
else if ((pCurrent->Usage == D3DDECLUSAGE_TEXCOORD)
&& (pCurrent->Type == D3DDECLTYPE_FLOAT2)
&& (pCurrent->UsageIndex == i))
{
fvfCode |= static_cast<uint32_t>(D3DFVF_TEXCOORDSIZE2(i));
}
else if ((pCurrent->Usage == D3DDECLUSAGE_TEXCOORD)
&& (pCurrent->Type == D3DDECLTYPE_FLOAT3)
&& (pCurrent->UsageIndex == i))
{
fvfCode |= static_cast<uint32_t>(D3DFVF_TEXCOORDSIZE3(i));
}
else if ((pCurrent->Usage == D3DDECLUSAGE_TEXCOORD)
&& (pCurrent->Type == D3DDECLTYPE_FLOAT4)
&& (pCurrent->UsageIndex == i))
{
fvfCode |= static_cast<uint32_t>(D3DFVF_TEXCOORDSIZE4(i));
}
else
break;
++count;
if (count > maxDeclLength)
return 0;
++pCurrent;
}
fvfCode |= static_cast<uint32_t>(i << D3DFVF_TEXCOUNT_SHIFT);
if (pCurrent->Stream != 0xff)
{
return 0;
}
return fvfCode;
}
}