HRESULT Mesh::ExportToCMO()

in UVAtlasTool/Mesh.cpp [1602:1968]


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;
}