in Meshconvert/Mesh.cpp [1386:1752]
HRESULT Mesh::ExportToCMO(const wchar_t* szFileName, size_t nMaterials, const Material* materials) const noexcept
{
using namespace VSD3DStarter;
if (!szFileName)
return E_INVALIDARG;
if (nMaterials > 0 && !materials)
return E_INVALIDARG;
if (!mnFaces || !mIndices || !mnVerts || !mPositions || !mNormals || !mTexCoords || !mTangents)
return E_UNEXPECTED;
if ((uint64_t(mnFaces) * 3) >= UINT32_MAX)
return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
if (mnVerts >= UINT16_MAX)
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
UINT nIndices = static_cast<UINT>(mnFaces * 3);
// Setup vertices/indices for CMO
std::unique_ptr<Vertex[]> vb(new (std::nothrow) Vertex[mnVerts]);
std::unique_ptr<uint16_t[]> ib(new (std::nothrow) uint16_t[nIndices]);
if (!vb || !ib)
return E_OUTOFMEMORY;
std::unique_ptr<SkinningVertex[]> vbSkin;
if (mBlendIndices && mBlendWeights)
{
vbSkin.reset(new (std::nothrow) SkinningVertex[mnVerts]);
if (!vbSkin)
return E_OUTOFMEMORY;
}
// Copy to VB
auto vptr = vb.get();
for (size_t j = 0; j < mnVerts; ++j, ++vptr)
{
vptr->Position = mPositions[j];
vptr->Normal = mNormals[j];
vptr->Tangent = mTangents[j];
vptr->TextureCoordinates = mTexCoords[j];
if (mColors)
{
XMVECTOR icolor = XMLoadFloat4(&mColors[j]);
PackedVector::XMUBYTEN4 rgba;
PackedVector::XMStoreUByteN4(&rgba, icolor);
vptr->color = rgba.v;
}
else
vptr->color = 0xFFFFFFFF;
}
// Copy to SkinVB
auto sptr = vbSkin.get();
if (sptr)
{
for (size_t j = 0; j < mnVerts; ++j, ++sptr)
{
XMVECTOR v = XMLoadFloat4(&mBlendIndices[j]);
XMStoreUInt4(reinterpret_cast<XMUINT4*>(&sptr->boneIndex[0]), v);
const XMFLOAT4* w = &mBlendWeights[j];
sptr->boneWeight[0] = w->x;
sptr->boneWeight[1] = w->y;
sptr->boneWeight[2] = w->z;
sptr->boneWeight[3] = w->w;
}
}
// Copy to IB
auto iptr = ib.get();
for (size_t j = 0; j < nIndices; ++j, ++iptr)
{
uint32_t index = mIndices[j];
if (index == uint32_t(-1))
{
*iptr = uint16_t(-1);
}
else if (index >= UINT16_MAX)
{
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
else
{
*iptr = static_cast<uint16_t>(index);
}
}
// Create CMO 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 1 mesh, name based on the filename
UINT n = 1;
HRESULT hr = write_file(hFile.get(), n);
if (FAILED(hr))
return hr;
{
wchar_t fname[_MAX_FNAME];
_wsplitpath_s(szFileName, nullptr, 0, nullptr, 0, fname, _MAX_FNAME, nullptr, 0);
hr = write_file_string(hFile.get(), fname);
if (FAILED(hr))
return hr;
}
// Write materials
static const Mesh::Material s_defMaterial = { L"default", false, 1.f, 1.f,
XMFLOAT3(0.2f, 0.2f, 0.2f), XMFLOAT3(0.8f, 0.8f, 0.8f),
XMFLOAT3(0.f, 0.f, 0.f), XMFLOAT3(0.f, 0.f, 0.f), L"" };
UINT materialCount = 1;
if (nMaterials > 0)
{
materialCount = static_cast<UINT>(nMaterials);
}
else
{
nMaterials = 1;
materials = &s_defMaterial;
}
hr = write_file(hFile.get(), materialCount);
if (FAILED(hr))
return hr;
for (UINT j = 0; j < materialCount; ++j)
{
auto& m = materials[j];
if (!m.name.empty())
{
hr = write_file_string(hFile.get(), m.name.c_str());
}
else
{
wchar_t name[64];
swprintf_s(name, L"material%03u\n", j);
hr = write_file_string(hFile.get(), name);
}
if (FAILED(hr))
return hr;
VSD3DStarter::Material mdata = {};
mdata.Ambient.x = m.ambientColor.x;
mdata.Ambient.y = m.ambientColor.y;
mdata.Ambient.z = m.ambientColor.z;
mdata.Ambient.w = 1.f;
mdata.Diffuse.x = m.diffuseColor.x;
mdata.Diffuse.y = m.diffuseColor.y;
mdata.Diffuse.z = m.diffuseColor.z;
mdata.Diffuse.w = m.alpha;
if (m.specularColor.x > 0.f || m.specularColor.y > 0.f || m.specularColor.z > 0.f)
{
mdata.Specular.x = m.specularColor.x;
mdata.Specular.y = m.specularColor.y;
mdata.Specular.z = m.specularColor.z;
mdata.SpecularPower = (m.specularPower <= 0.f) ? 16.f : m.specularPower;
}
else
{
mdata.SpecularPower = 1.f;
}
mdata.Specular.w = 1.f;
mdata.Emissive.x = m.emissiveColor.x;
mdata.Emissive.y = m.emissiveColor.y;
mdata.Emissive.z = m.emissiveColor.z;
mdata.Emissive.w = 1.f;
XMMATRIX id = XMMatrixIdentity();
XMStoreFloat4x4(&mdata.UVTransform, id);
hr = write_file(hFile.get(), mdata);
if (FAILED(hr))
return hr;
if (m.specularColor.x > 0.f || m.specularColor.y > 0.f || m.specularColor.z > 0.f)
{
hr = write_file_string(hFile.get(), L"phong.dgsl");
}
else
{
hr = write_file_string(hFile.get(), L"lambert.dgsl");
}
if (FAILED(hr))
return hr;
hr = write_file_string(hFile.get(), m.texture.c_str());
if (FAILED(hr))
return hr;
for (size_t k = 1; k < MAX_TEXTURE; ++k)
{
hr = write_file_string(hFile.get(), L"");
if (FAILED(hr))
return hr;
}
}
BYTE sd = 0; // No skeleton/animation data
hr = write_file(hFile.get(), sd);
if (FAILED(hr))
return hr;
if (mAttributes)
{
auto subsets = ComputeSubsets(mAttributes.get(), mnFaces);
n = static_cast<UINT>(subsets.size());
hr = write_file(hFile.get(), n);
if (FAILED(hr))
return hr;
size_t startIndex = 0;
for (const auto& it : subsets)
{
SubMesh smesh;
smesh.MaterialIndex = mAttributes[it.first];
if (smesh.MaterialIndex >= nMaterials)
smesh.MaterialIndex = 0;
smesh.IndexBufferIndex = 0;
smesh.VertexBufferIndex = 0;
smesh.StartIndex = static_cast<UINT>(startIndex);
smesh.PrimCount = static_cast<UINT>(it.second);
hr = write_file(hFile.get(), smesh);
if (FAILED(hr))
return hr;
if ((startIndex + (it.second * 3)) > mnFaces * 3)
return E_FAIL;
startIndex += static_cast<size_t>(uint64_t(smesh.PrimCount) * 3);
}
}
else
{
n = 1;
hr = write_file(hFile.get(), n);
if (FAILED(hr))
return hr;
SubMesh smesh;
smesh.MaterialIndex = 0;
smesh.IndexBufferIndex = 0;
smesh.VertexBufferIndex = 0;
smesh.StartIndex = 0;
smesh.PrimCount = static_cast<UINT>(mnFaces);
hr = write_file(hFile.get(), smesh);
if (FAILED(hr))
return hr;
}
// Write indices (one IB shared across submeshes)
n = 1;
hr = write_file(hFile.get(), n);
if (FAILED(hr))
return hr;
hr = write_file(hFile.get(), nIndices);
if (FAILED(hr))
return hr;
auto indexSize = static_cast<DWORD>(sizeof(uint16_t) * nIndices);
DWORD bytesWritten;
if (!WriteFile(hFile.get(), ib.get(), indexSize, &bytesWritten, nullptr))
return HRESULT_FROM_WIN32(GetLastError());
if (bytesWritten != indexSize)
return E_FAIL;
// Write vertices (one VB shared across submeshes)
n = 1;
hr = write_file(hFile.get(), n);
if (FAILED(hr))
return hr;
n = static_cast<UINT>(mnVerts);
hr = write_file(hFile.get(), n);
if (FAILED(hr))
return hr;
auto vertSize = static_cast<DWORD>(sizeof(Vertex) * mnVerts);
if (!WriteFile(hFile.get(), vb.get(), vertSize, &bytesWritten, nullptr))
return HRESULT_FROM_WIN32(GetLastError());
if (bytesWritten != vertSize)
return E_FAIL;
// Write skinning vertices (one SkinVB shared across submeshes)
if (vbSkin)
{
n = 1;
hr = write_file(hFile.get(), n);
if (FAILED(hr))
return hr;
n = static_cast<UINT>(mnVerts);
hr = write_file(hFile.get(), n);
if (FAILED(hr))
return hr;
auto skinVertSize = static_cast<DWORD>(sizeof(SkinningVertex) * mnVerts);
if (!WriteFile(hFile.get(), vbSkin.get(), skinVertSize, &bytesWritten, nullptr))
return HRESULT_FROM_WIN32(GetLastError());
if (bytesWritten != skinVertSize)
return E_FAIL;
}
else
{
n = 0;
hr = write_file(hFile.get(), n);
if (FAILED(hr))
return hr;
}
// Write extents
{
BoundingSphere sphere;
BoundingSphere::CreateFromPoints(sphere, mnVerts, mPositions.get(), sizeof(XMFLOAT3));
BoundingBox box;
BoundingBox::CreateFromPoints(box, mnVerts, mPositions.get(), sizeof(XMFLOAT3));
MeshExtents extents;
extents.CenterX = sphere.Center.x;
extents.CenterY = sphere.Center.y;
extents.CenterZ = sphere.Center.z;
extents.Radius = sphere.Radius;
extents.MinX = box.Center.x - box.Extents.x;
extents.MinY = box.Center.y - box.Extents.y;
extents.MinZ = box.Center.z - box.Extents.z;
extents.MaxX = box.Center.x + box.Extents.x;
extents.MaxY = box.Center.y + box.Extents.y;
extents.MaxZ = box.Center.z + box.Extents.z;
hr = write_file(hFile.get(), extents);
if (FAILED(hr))
return hr;
}
// No skeleton data, so no animations
return S_OK;
}