in Meshconvert/Mesh.cpp [1761:2434]
HRESULT Mesh::ExportToSDKMESH(const wchar_t* szFileName,
size_t nMaterials, const Material* materials,
bool force32bit,
bool version2,
DXGI_FORMAT normalFormat,
DXGI_FORMAT uvFormat,
DXGI_FORMAT colorFormat) const noexcept
{
using namespace DXUT;
if (!szFileName)
return E_INVALIDARG;
if (nMaterials > 0 && !materials)
return E_INVALIDARG;
if (!mnFaces || !mIndices || !mnVerts || !mPositions)
return E_UNEXPECTED;
if ((uint64_t(mnFaces) * 3) >= UINT32_MAX)
return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
// Build input layout/vertex decalaration
static const D3D11_INPUT_ELEMENT_DESC s_elements[] =
{
{ "SV_Position", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, // 0
{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, // 1
{ "COLOR", 0, DXGI_FORMAT_B8G8R8A8_UNORM, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, // 2
{ "TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, // 3
{ "BINORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, // 4
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, // 5
{ "BLENDINDICES", 0, DXGI_FORMAT_R8G8B8A8_UINT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, // 6
{ "BLENDWEIGHT", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, // 7
};
static const D3DVERTEXELEMENT9 s_decls[] =
{
{ 0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0 }, // 0
{ 0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0 }, // 1
{ 0, 0, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0 }, // 2
{ 0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_TANGENT, 0 }, // 3
{ 0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_BINORMAL, 0 }, // 4
{ 0, 0, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_TEXCOORD, 0 }, // 5
{ 0, 0, D3DDECLTYPE_UBYTE4, 0, D3DDECLUSAGE_BLENDINDICES, 0 }, // 6
{ 0, 0, D3DDECLTYPE_UBYTE4N, 0, D3DDECLUSAGE_BLENDWEIGHT, 0 }, // 7
{ 0xFF, 0, D3DDECLTYPE_UNUSED, 0, 0, 0 },
};
static_assert((std::size(s_elements) + 1) == std::size(s_decls), "InputLayouts and Vertex Decls disagree");
uint8_t normalType;
size_t normalStride;
switch (normalFormat)
{
case DXGI_FORMAT_R16G16B16A16_FLOAT:
normalType = D3DDECLTYPE_FLOAT16_4; normalStride = sizeof(PackedVector::XMHALF4);
break;
case DXGI_FORMAT_R11G11B10_FLOAT: // Biased in GetVertexBuffer
normalType = D3DDECLTYPE_DXGI_R11G11B10_FLOAT; normalStride = sizeof(UINT);
break;
default:
normalFormat = DXGI_FORMAT_R32G32B32_FLOAT; normalType = D3DDECLTYPE_FLOAT3; normalStride = sizeof(XMFLOAT3);
break;
}
uint8_t uvType;
size_t uvStride;
switch (uvFormat)
{
case DXGI_FORMAT_R16G16_FLOAT:
uvType = D3DDECLTYPE_FLOAT16_2; uvStride = sizeof(PackedVector::XMHALF2);
break;
default:
uvFormat = DXGI_FORMAT_R32G32_FLOAT; uvType = D3DDECLTYPE_FLOAT2; uvStride = sizeof(XMFLOAT2);
break;
}
uint8_t colorType;
size_t colorStride;
switch (colorFormat)
{
case DXGI_FORMAT_R32G32B32A32_FLOAT:
colorType = D3DDECLTYPE_FLOAT4; colorStride = sizeof(XMFLOAT4);
break;
case DXGI_FORMAT_R16G16B16A16_FLOAT:
colorType = D3DDECLTYPE_FLOAT16_4; colorStride = sizeof(PackedVector::XMHALF4);
break;
case DXGI_FORMAT_R11G11B10_FLOAT:
colorType = D3DDECLTYPE_DXGI_R11G11B10_FLOAT; colorStride = sizeof(UINT);
break;
case DXGI_FORMAT_R10G10B10A2_UNORM:
colorType = D3DDECLTYPE_DXGI_R10G10B10A2_UNORM; colorStride = sizeof(UINT);
break;
case DXGI_FORMAT_R8G8B8A8_UNORM:
colorType = D3DDECLTYPE_UBYTE4N; colorStride = sizeof(UINT);
break;
default:
colorFormat = DXGI_FORMAT_B8G8R8A8_UNORM; colorType = D3DDECLTYPE_D3DCOLOR; colorStride = sizeof(UINT);
break;
}
SDKMESH_VERTEX_BUFFER_HEADER vbHeader = {};
vbHeader.NumVertices = mnVerts;
vbHeader.Decl[0] = s_decls[0];
D3D11_INPUT_ELEMENT_DESC inputLayout[MAX_VERTEX_ELEMENTS] = {};
inputLayout[0] = s_elements[0];
size_t nDecl = 1;
size_t stride = sizeof(XMFLOAT3);
if (mBlendIndices && mBlendWeights)
{
// BLENDWEIGHT
vbHeader.Decl[nDecl] = s_decls[7];
vbHeader.Decl[nDecl].Offset = static_cast<WORD>(stride);
inputLayout[nDecl] = s_elements[7];
++nDecl;
stride += sizeof(UINT);
// BLENDINDICES
vbHeader.Decl[nDecl] = s_decls[6];
vbHeader.Decl[nDecl].Offset = static_cast<WORD>(stride);
inputLayout[nDecl] = s_elements[6];
++nDecl;
stride += sizeof(UINT);
}
if (mNormals)
{
vbHeader.Decl[nDecl] = s_decls[1];
vbHeader.Decl[nDecl].Type = normalType;
vbHeader.Decl[nDecl].Offset = static_cast<WORD>(stride);
inputLayout[nDecl] = s_elements[1];
inputLayout[nDecl].Format = normalFormat;
++nDecl;
stride += normalStride;
}
if (mColors)
{
vbHeader.Decl[nDecl] = s_decls[2];
vbHeader.Decl[nDecl].Type = colorType;
vbHeader.Decl[nDecl].Offset = static_cast<WORD>(stride);
inputLayout[nDecl] = s_elements[2];
inputLayout[nDecl].Format = colorFormat;
++nDecl;
stride += colorStride;
}
if (mTexCoords)
{
vbHeader.Decl[nDecl] = s_decls[5];
vbHeader.Decl[nDecl].Type = uvType;
vbHeader.Decl[nDecl].Offset = static_cast<WORD>(stride);
inputLayout[nDecl] = s_elements[5];
inputLayout[nDecl].Format = uvFormat;
++nDecl;
stride += uvStride;
}
if (mTangents)
{
vbHeader.Decl[nDecl] = s_decls[3];
vbHeader.Decl[nDecl].Type = normalType;
vbHeader.Decl[nDecl].Offset = static_cast<WORD>(stride);
inputLayout[nDecl] = s_elements[3];
inputLayout[nDecl].Format = normalFormat;
++nDecl;
stride += normalStride;
}
if (mBiTangents)
{
vbHeader.Decl[nDecl] = s_decls[4];
vbHeader.Decl[nDecl].Type = normalType;
vbHeader.Decl[nDecl].Offset = static_cast<WORD>(stride);
inputLayout[nDecl] = s_elements[4];
inputLayout[nDecl].Format = normalFormat;
++nDecl;
stride += normalStride;
}
assert(nDecl < MAX_VERTEX_ELEMENTS);
vbHeader.Decl[nDecl] = s_decls[std::size(s_decls) - 1];
// Build vertex buffer
std::unique_ptr<uint8_t> vb(new (std::nothrow) uint8_t[mnVerts * stride]);
if (!vb)
return E_OUTOFMEMORY;
vbHeader.SizeBytes = uint64_t(mnVerts) * uint64_t(stride);
vbHeader.StrideBytes = stride;
{
VBWriter writer;
HRESULT hr = writer.Initialize(inputLayout, nDecl);
if (FAILED(hr))
return hr;
hr = writer.AddStream(vb.get(), mnVerts, 0, stride);
if (FAILED(hr))
return hr;
hr = GetVertexBuffer(writer);
if (FAILED(hr))
return hr;
}
// Build index buffer
SDKMESH_INDEX_BUFFER_HEADER ibHeader = {};
ibHeader.NumIndices = uint64_t(mnFaces) * 3;
std::unique_ptr<uint16_t[]> ib16;
if (!force32bit && Is16BitIndexBuffer())
{
ibHeader.SizeBytes = uint64_t(mnFaces) * 3 * sizeof(uint16_t);
ibHeader.IndexType = IT_16BIT;
ib16 = GetIndexBuffer16();
if (!ib16)
return E_OUTOFMEMORY;
}
else
{
ibHeader.SizeBytes = uint64_t(mnFaces) * 3 * sizeof(uint32_t);
ibHeader.IndexType = IT_32BIT;
}
// Build materials buffer
std::unique_ptr<SDKMESH_MATERIAL[]> mats;
if (version2)
{
if (!nMaterials)
{
mats.reset(new (std::nothrow) SDKMESH_MATERIAL[1]);
if (!mats)
return E_OUTOFMEMORY;
auto mat2 = reinterpret_cast<SDKMESH_MATERIAL_V2*>(mats.get());
memset(mat2, 0, sizeof(SDKMESH_MATERIAL_V2));
strcpy_s(mat2->Name, "default");
mat2->Alpha = 1.f;
}
else
{
mats.reset(new (std::nothrow) SDKMESH_MATERIAL[nMaterials]);
if (!mats)
return E_OUTOFMEMORY;
for (size_t j = 0; j < nMaterials; ++j)
{
auto m0 = &materials[j];
auto m2 = reinterpret_cast<SDKMESH_MATERIAL_V2*>(&mats[j]);
memset(m2, 0, sizeof(SDKMESH_MATERIAL_V2));
if (!m0->name.empty())
{
int result = WideCharToMultiByte(CP_UTF8, WC_NO_BEST_FIT_CHARS,
m0->name.c_str(), -1,
m2->Name, MAX_MATERIAL_NAME, nullptr, FALSE);
if (!result)
{
*m2->Name = 0;
}
}
m2->Alpha = m0->alpha;
if (!m0->texture.empty())
{
int result = WideCharToMultiByte(CP_UTF8, WC_NO_BEST_FIT_CHARS,
m0->texture.c_str(), -1,
m2->AlbedoTexture, MAX_TEXTURE_NAME, nullptr, FALSE);
if (!result)
{
*m2->AlbedoTexture = 0;
}
}
// Derive other PBR texture names from base texture
{
char drive[_MAX_DRIVE] = {};
char dir[MAX_PATH] = {};
char fname[_MAX_FNAME] = {};
char ext[_MAX_EXT] = {};
_splitpath_s(m2->AlbedoTexture, drive, dir, fname, ext);
std::string basename = fname;
size_t pos = basename.find_last_of('_');
if (pos != std::string::npos)
{
basename = basename.substr(0, pos);
}
if (!basename.empty())
{
strcpy_s(fname, basename.c_str());
strcat_s(fname, "_normal");
_makepath_s(m2->NormalTexture, drive, dir, fname, ext);
strcpy_s(fname, basename.c_str());
strcat_s(fname, "_occlusionRoughnessMetallic");
_makepath_s(m2->RMATexture, drive, dir, fname, ext);
if (m0->emissiveColor.x > 0 || m0->emissiveColor.y > 0 || m0->emissiveColor.z > 0)
{
strcpy_s(fname, basename.c_str());
strcat_s(fname, "_emissive");
_makepath_s(m2->EmissiveTexture, drive, dir, fname, ext);
}
}
}
// Allow normal texture material property to override derived name
if (!m0->normalTexture.empty())
{
int result = WideCharToMultiByte(CP_UTF8, WC_NO_BEST_FIT_CHARS,
m0->normalTexture.c_str(), -1,
m2->NormalTexture, MAX_TEXTURE_NAME, nullptr, FALSE);
if (!result)
{
*m2->NormalTexture = 0;
}
}
// Allow emissive texture material property to override drived name
if (!m0->emissiveTexture.empty())
{
int result = WideCharToMultiByte(CP_UTF8, WC_NO_BEST_FIT_CHARS,
m0->emissiveTexture.c_str(), -1,
m2->EmissiveTexture, MAX_TEXTURE_NAME, nullptr, FALSE);
if (!result)
{
*m2->EmissiveTexture = 0;
}
}
// Allow RMA texture material property to override drived name
if (!m0->rmaTexture.empty())
{
int result = WideCharToMultiByte(CP_UTF8, WC_NO_BEST_FIT_CHARS,
m0->rmaTexture.c_str(), -1,
m2->RMATexture, MAX_TEXTURE_NAME, nullptr, FALSE);
if (!result)
{
*m2->RMATexture = 0;
}
}
}
}
}
else if (!nMaterials)
{
mats.reset(new (std::nothrow) SDKMESH_MATERIAL[1]);
if (!mats)
return E_OUTOFMEMORY;
memset(mats.get(), 0, sizeof(SDKMESH_MATERIAL));
strcpy_s(mats[0].Name, "default");
mats[0].Diffuse = XMFLOAT4(0.8f, 0.8f, 0.8f, 1.f);
mats[0].Ambient = XMFLOAT4(0.2f, 02.f, 0.2f, 1.f);
mats[0].Power = 1.f;
}
else
{
mats.reset(new (std::nothrow) SDKMESH_MATERIAL[nMaterials]);
if (!mats)
return E_OUTOFMEMORY;
for (size_t j = 0; j < nMaterials; ++j)
{
auto m0 = &materials[j];
auto m = &mats[j];
memset(m, 0, sizeof(SDKMESH_MATERIAL));
if (!m0->name.empty())
{
int result = WideCharToMultiByte(CP_UTF8, WC_NO_BEST_FIT_CHARS,
m0->name.c_str(), -1,
m->Name, MAX_MATERIAL_NAME, nullptr, FALSE);
if (!result)
{
*m->Name = 0;
}
}
if (!m0->texture.empty())
{
int result = WideCharToMultiByte(CP_UTF8, WC_NO_BEST_FIT_CHARS,
m0->texture.c_str(), -1,
m->DiffuseTexture, MAX_TEXTURE_NAME, nullptr, FALSE);
if (!result)
{
*m->DiffuseTexture = 0;
}
}
if (!m0->normalTexture.empty())
{
int result = WideCharToMultiByte(CP_UTF8, WC_NO_BEST_FIT_CHARS,
m0->normalTexture.c_str(), -1,
m->NormalTexture, MAX_TEXTURE_NAME, nullptr, FALSE);
if (!result)
{
*m->NormalTexture = 0;
}
}
if (!m0->specularTexture.empty())
{
int result = WideCharToMultiByte(CP_UTF8, WC_NO_BEST_FIT_CHARS,
m0->specularTexture.c_str(), -1,
m->SpecularTexture, MAX_TEXTURE_NAME, nullptr, FALSE);
if (!result)
{
*m->SpecularTexture = 0;
}
}
m->Diffuse.x = m0->diffuseColor.x;
m->Diffuse.y = m0->diffuseColor.y;
m->Diffuse.z = m0->diffuseColor.z;
m->Diffuse.w = m0->alpha;
m->Ambient.x = m0->ambientColor.x;
m->Ambient.y = m0->ambientColor.y;
m->Ambient.z = m0->ambientColor.z;
m->Ambient.w = 1.f;
if (m0->specularColor.x > 0.f || m0->specularColor.y > 0.f || m0->specularColor.z > 0.f)
{
m->Specular.x = m0->specularColor.x;
m->Specular.y = m0->specularColor.y;
m->Specular.z = m0->specularColor.z;
m->Power = (m0->specularPower <= 0.f) ? 16.f : m0->specularPower;
}
else
{
m->Power = 1.f;
}
m->Emissive.x = m0->emissiveColor.x;
m->Emissive.y = m0->emissiveColor.y;
m->Emissive.z = m0->emissiveColor.z;
}
}
// Build subsets
std::vector<SDKMESH_SUBSET> submeshes;
std::vector<UINT> subsetArray;
if (mAttributes)
{
auto subsets = ComputeSubsets(mAttributes.get(), mnFaces);
UINT64 startIndex = 0;
for (const auto& it : subsets)
{
subsetArray.push_back(static_cast<UINT>(submeshes.size()));
SDKMESH_SUBSET s = {};
s.MaterialID = mAttributes[it.first];
if (s.MaterialID >= nMaterials)
s.MaterialID = 0;
s.PrimitiveType = PT_TRIANGLE_LIST;
s.IndexStart = startIndex;
s.IndexCount = uint64_t(it.second) * 3;
s.VertexCount = mnVerts;
submeshes.push_back(s);
if ((startIndex + s.IndexCount) > uint64_t(mnFaces) * 3)
return E_FAIL;
startIndex += s.IndexCount;
}
}
else
{
SDKMESH_SUBSET s = {};
s.PrimitiveType = PT_TRIANGLE_LIST;
s.IndexCount = uint64_t(mnFaces) * 3;
s.VertexCount = mnVerts;
subsetArray.push_back(0);
submeshes.push_back(s);
}
// Create file
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
ScopedHandle hFile(safe_handle(CreateFile2(szFileName,
GENERIC_WRITE, 0, CREATE_ALWAYS, nullptr)));
#else
ScopedHandle hFile(safe_handle(CreateFileW(szFileName,
GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr)));
#endif
if (!hFile)
return HRESULT_FROM_WIN32(GetLastError());
// Write file header
SDKMESH_HEADER header = {};
header.Version = (version2) ? SDKMESH_FILE_VERSION_V2 : SDKMESH_FILE_VERSION;
header.IsBigEndian = 0;
header.NumVertexBuffers = 1;
header.NumIndexBuffers = 1;
header.NumMeshes = 1;
header.NumTotalSubsets = static_cast<UINT>(submeshes.size());
header.NumFrames = 1;
header.NumMaterials = (nMaterials > 0) ? static_cast<UINT>(nMaterials) : 1;
header.HeaderSize = sizeof(SDKMESH_HEADER) + sizeof(SDKMESH_VERTEX_BUFFER_HEADER) + sizeof(SDKMESH_INDEX_BUFFER_HEADER);
size_t staticDataSize = sizeof(SDKMESH_MESH)
+ header.NumTotalSubsets * sizeof(SDKMESH_SUBSET)
+ sizeof(SDKMESH_FRAME)
+ header.NumMaterials * sizeof(SDKMESH_MATERIAL);
header.NonBufferDataSize = uint64_t(staticDataSize) + uint64_t(subsetArray.size()) * sizeof(UINT) + sizeof(UINT);
header.BufferDataSize = roundup4k(vbHeader.SizeBytes) + roundup4k(ibHeader.SizeBytes);
header.VertexStreamHeadersOffset = sizeof(SDKMESH_HEADER);
header.IndexStreamHeadersOffset = header.VertexStreamHeadersOffset + sizeof(SDKMESH_VERTEX_BUFFER_HEADER);
header.MeshDataOffset = header.IndexStreamHeadersOffset + sizeof(SDKMESH_INDEX_BUFFER_HEADER);
header.SubsetDataOffset = header.MeshDataOffset + sizeof(SDKMESH_MESH);
header.FrameDataOffset = header.SubsetDataOffset + uint64_t(header.NumTotalSubsets) * sizeof(SDKMESH_SUBSET);
header.MaterialDataOffset = header.FrameDataOffset + sizeof(SDKMESH_FRAME);
HRESULT hr = write_file(hFile.get(), header);
if (FAILED(hr))
return hr;
// Write buffer headers
UINT64 offset = header.HeaderSize + header.NonBufferDataSize;
vbHeader.DataOffset = offset;
offset += roundup4k(vbHeader.SizeBytes);
hr = write_file(hFile.get(), vbHeader);
if (FAILED(hr))
return hr;
ibHeader.DataOffset = offset;
offset += roundup4k(ibHeader.SizeBytes);
hr = write_file(hFile.get(), ibHeader);
if (FAILED(hr))
return hr;
// Write mesh headers
assert(header.NumMeshes == 1);
offset = header.HeaderSize + staticDataSize;
SDKMESH_MESH meshHeader = {};
meshHeader.NumVertexBuffers = 1;
meshHeader.NumFrameInfluences = 1;
{
BoundingBox box;
BoundingBox::CreateFromPoints(box, mnVerts, mPositions.get(), sizeof(XMFLOAT3));
meshHeader.BoundingBoxCenter = box.Center;
meshHeader.BoundingBoxExtents = box.Extents;
}
meshHeader.NumSubsets = static_cast<UINT>(submeshes.size());
meshHeader.SubsetOffset = offset;
offset += uint64_t(meshHeader.NumSubsets) * sizeof(UINT);
meshHeader.FrameInfluenceOffset = offset;
offset += sizeof(UINT);
hr = write_file(hFile.get(), meshHeader);
if (FAILED(hr))
return hr;
// Write subsets
auto bytesToWrite = static_cast<DWORD>(sizeof(SDKMESH_SUBSET) * submeshes.size());
DWORD bytesWritten;
if (!WriteFile(hFile.get(), submeshes.data(), bytesToWrite, &bytesWritten, nullptr))
return HRESULT_FROM_WIN32(GetLastError());
if (bytesWritten != bytesToWrite)
return E_FAIL;
// Write frames
SDKMESH_FRAME frame = {};
strcpy_s(frame.Name, "root");
frame.ParentFrame = frame.ChildFrame = frame.SiblingFrame = DWORD(-1);
frame.AnimationDataIndex = INVALID_ANIMATION_DATA;
XMMATRIX id = XMMatrixIdentity();
XMStoreFloat4x4(&frame.Matrix, id);
hr = write_file(hFile.get(), frame);
if (FAILED(hr))
return hr;
// Write materials
bytesToWrite = static_cast<DWORD>(sizeof(SDKMESH_MATERIAL) * ((nMaterials > 0) ? nMaterials : 1));
if (!WriteFile(hFile.get(), mats.get(), bytesToWrite, &bytesWritten, nullptr))
return HRESULT_FROM_WIN32(GetLastError());
if (bytesWritten != bytesToWrite)
return E_FAIL;
// Write subset index list
assert(meshHeader.NumSubsets == subsetArray.size());
bytesToWrite = meshHeader.NumSubsets * sizeof(UINT);
if (!WriteFile(hFile.get(), subsetArray.data(), bytesToWrite, &bytesWritten, nullptr))
return HRESULT_FROM_WIN32(GetLastError());
if (bytesWritten != bytesToWrite)
return E_FAIL;
// Write frame influence list
assert(meshHeader.NumFrameInfluences == 1);
UINT frameIndex = 0;
hr = write_file(hFile.get(), frameIndex);
if (FAILED(hr))
return hr;
// Write VB data
bytesToWrite = static_cast<DWORD>(vbHeader.SizeBytes);
if (!WriteFile(hFile.get(), vb.get(), bytesToWrite, &bytesWritten, nullptr))
return HRESULT_FROM_WIN32(GetLastError());
if (bytesWritten != bytesToWrite)
return E_FAIL;
bytesToWrite = static_cast<DWORD>(roundup4k(vbHeader.SizeBytes) - vbHeader.SizeBytes);
if (bytesToWrite > 0)
{
assert(bytesToWrite < sizeof(g_padding));
if (!WriteFile(hFile.get(), g_padding, bytesToWrite, &bytesWritten, nullptr))
return HRESULT_FROM_WIN32(GetLastError());
if (bytesWritten != bytesToWrite)
return E_FAIL;
}
// Write IB data
bytesToWrite = static_cast<DWORD>(ibHeader.SizeBytes);
if (!WriteFile(hFile.get(), (ib16) ? static_cast<void*>(ib16.get()) : static_cast<void*>(mIndices.get()),
bytesToWrite, &bytesWritten, nullptr))
return HRESULT_FROM_WIN32(GetLastError());
if (bytesWritten != bytesToWrite)
return E_FAIL;
bytesToWrite = static_cast<DWORD>(roundup4k(ibHeader.SizeBytes) - ibHeader.SizeBytes);
if (bytesToWrite > 0)
{
assert(bytesToWrite < sizeof(g_padding));
if (!WriteFile(hFile.get(), g_padding, bytesToWrite, &bytesWritten, nullptr))
return HRESULT_FROM_WIN32(GetLastError());
if (bytesWritten != bytesToWrite)
return E_FAIL;
}
return S_OK;
}