示例#1
0
// Creates an effect for a mesh part
_Use_decl_annotations_
std::shared_ptr<IEffect> Model::CreateEffectForMeshPart(
    IEffectFactory& fxFactory, 
    const EffectPipelineStateDescription& opaquePipelineState,
    const EffectPipelineStateDescription& alphaPipelineState, 
    int textureDescriptorOffset,
    int samplerDescriptorOffset,
    const ModelMeshPart* part) const
{
    assert(part->materialIndex < materials.size());

    const auto& m = materials[part->materialIndex];

    D3D12_INPUT_LAYOUT_DESC il = {};
    il.NumElements = (uint32_t) part->vbDecl->size();
    il.pInputElementDescs = part->vbDecl->data();

    return fxFactory.CreateEffect(m, opaquePipelineState, alphaPipelineState, il, textureDescriptorOffset, samplerDescriptorOffset);
}
示例#2
0
// Creates an effect for a mesh part
_Use_decl_annotations_
std::shared_ptr<IEffect> Model::CreateEffectForMeshPart(
    IEffectFactory& fxFactory,
    const EffectPipelineStateDescription& opaquePipelineState,
    const EffectPipelineStateDescription& alphaPipelineState,
    int textureDescriptorOffset,
    int samplerDescriptorOffset,
    const ModelMeshPart* part) const
{
    assert(part->materialIndex < materials.size());
    const auto& m = materials[part->materialIndex];

    if (!part->vbDecl || part->vbDecl->empty())
        throw std::exception("Model mesh part missing vertex buffer input elements data");

    if (part->vbDecl->size() > D3D12_IA_VERTEX_INPUT_STRUCTURE_ELEMENT_COUNT)
        throw std::exception("Model mesh part input layout size is too large for DirectX 12");

    D3D12_INPUT_LAYOUT_DESC il = {};
    il.NumElements = static_cast<UINT>(part->vbDecl->size());
    il.pInputElementDescs = part->vbDecl->data();

    return fxFactory.CreateEffect(m, opaquePipelineState, alphaPipelineState, il, textureDescriptorOffset, samplerDescriptorOffset);
}
//--------------------------------------------------------------------------------------
_Use_decl_annotations_
std::unique_ptr<Model> DirectX::Model::CreateFromCMO( ID3D11Device* d3dDevice, const uint8_t* meshData, size_t dataSize, IEffectFactory& fxFactory, bool ccw, bool pmalpha )
{
    if ( !InitOnceExecuteOnce( &g_InitOnce, InitializeDecl, nullptr, nullptr ) )
        throw std::exception("One-time initialization failed");
    
    if ( !d3dDevice || !meshData )
        throw std::exception("Device and meshData cannot be null");

    auto fxFactoryDGSL = dynamic_cast<DGSLEffectFactory*>( &fxFactory );

    // Meshes
    auto nMesh = reinterpret_cast<const UINT*>( meshData );
    size_t usedSize = sizeof(UINT);
    if ( dataSize < usedSize )
        throw std::exception("End of file");

    if ( !*nMesh )
        throw std::exception("No meshes found");

    std::unique_ptr<Model> model(new Model());

    for( UINT meshIndex = 0; meshIndex < *nMesh; ++meshIndex )
    {
        // Mesh name
        auto nName = reinterpret_cast<const UINT*>( meshData + usedSize );
        usedSize += sizeof(UINT);
        if ( dataSize < usedSize )
            throw std::exception("End of file");

        auto meshName = reinterpret_cast<const wchar_t*>( meshData + usedSize );

        usedSize += sizeof(wchar_t)*(*nName);
        if ( dataSize < usedSize )
            throw std::exception("End of file");

        auto mesh = std::make_shared<ModelMesh>();
        mesh->name.assign( meshName, *nName );
        mesh->ccw = ccw;
        mesh->pmalpha = pmalpha;

        // Materials
        auto nMats = reinterpret_cast<const UINT*>( meshData + usedSize );
        usedSize += sizeof(UINT);
        if ( dataSize < usedSize )
            throw std::exception("End of file");

        std::vector<MaterialRecordCMO> materials;
        materials.reserve( *nMats );
        for( UINT j = 0; j < *nMats; ++j )
        {
            MaterialRecordCMO m;

            // Material name
            nName = reinterpret_cast<const UINT*>( meshData + usedSize );
            usedSize += sizeof(UINT);
            if ( dataSize < usedSize )
                throw std::exception("End of file");

            auto matName = reinterpret_cast<const wchar_t*>( meshData + usedSize );

            usedSize += sizeof(wchar_t)*(*nName);
            if ( dataSize < usedSize )
                throw std::exception("End of file");

            m.name.assign( matName, *nName );

            // Material settings
            auto matSetting = reinterpret_cast<const VSD3DStarter::Material*>( meshData + usedSize );
            usedSize += sizeof(VSD3DStarter::Material);
            if ( dataSize < usedSize )
                throw std::exception("End of file");

            m.pMaterial = matSetting;

            // Pixel shader name
            nName = reinterpret_cast<const UINT*>( meshData + usedSize );
            usedSize += sizeof(UINT);
            if ( dataSize < usedSize )
                throw std::exception("End of file");

            auto psName = reinterpret_cast<const wchar_t*>( meshData + usedSize );

            usedSize += sizeof(wchar_t)*(*nName);
            if ( dataSize < usedSize )
                throw std::exception("End of file");

            m.pixelShader.assign( psName, *nName );

            for( UINT t = 0; t < VSD3DStarter::MAX_TEXTURE; ++t )
            {
                nName = reinterpret_cast<const UINT*>( meshData + usedSize );
                usedSize += sizeof(UINT);
                if ( dataSize < usedSize )
                    throw std::exception("End of file");

                auto txtName = reinterpret_cast<const wchar_t*>( meshData + usedSize );

                usedSize += sizeof(wchar_t)*(*nName);
                if ( dataSize < usedSize )
                    throw std::exception("End of file");

                m.texture[t].assign( txtName, *nName );
            }

            materials.emplace_back( m );
        }

        assert( materials.size() == *nMats );

        // Skeletal data?
        auto bSkeleton = reinterpret_cast<const BYTE*>( meshData + usedSize );
        usedSize += sizeof(BYTE);
        if ( dataSize < usedSize )
            throw std::exception("End of file");

        // Submeshes
        auto nSubmesh = reinterpret_cast<const UINT*>( meshData + usedSize );
        usedSize += sizeof(UINT);
        if ( dataSize < usedSize )
            throw std::exception("End of file");

        if ( !*nSubmesh )
            throw std::exception("No submeshes found\n");

        auto subMesh = reinterpret_cast<const VSD3DStarter::SubMesh*>( meshData + usedSize );
        usedSize += sizeof(VSD3DStarter::SubMesh) * (*nSubmesh);
        if ( dataSize < usedSize )
            throw std::exception("End of file");

        // Index buffers
        auto nIBs = reinterpret_cast<const UINT*>( meshData + usedSize );
        usedSize += sizeof(UINT);
        if ( dataSize < usedSize )
            throw std::exception("End of file");

        if ( !*nIBs )
            throw std::exception("No index buffers found\n");

        struct IBData
        {
            size_t          nIndices;
            const USHORT*   ptr;
        };

        std::vector<IBData> ibData;
        ibData.reserve( *nIBs );

        std::vector<ComPtr<ID3D11Buffer>> ibs;
        ibs.resize( *nIBs );

        for( UINT j = 0; j < *nIBs; ++j )
        {
            auto nIndexes = reinterpret_cast<const UINT*>( meshData + usedSize );
            usedSize += sizeof(UINT);
            if ( dataSize < usedSize )
                throw std::exception("End of file");

            if ( !*nIndexes )
                throw std::exception("Empty index buffer found\n");

            size_t ibBytes = sizeof(USHORT) * (*(nIndexes));

            auto indexes = reinterpret_cast<const USHORT*>( meshData + usedSize );
            usedSize += ibBytes;
            if ( dataSize < usedSize )
                throw std::exception("End of file");

            IBData ib;
            ib.nIndices = *nIndexes;
            ib.ptr = indexes;
            ibData.emplace_back( ib );

            D3D11_BUFFER_DESC desc = {0};
            desc.Usage = D3D11_USAGE_DEFAULT;
            desc.ByteWidth = static_cast<UINT>( ibBytes );
            desc.BindFlags = D3D11_BIND_INDEX_BUFFER;

            D3D11_SUBRESOURCE_DATA initData = {0};
            initData.pSysMem = indexes;

            ThrowIfFailed(
                d3dDevice->CreateBuffer( &desc, &initData, &ibs[j] )
                );

            SetDebugObjectName( ibs[j].Get(), "ModelCMO" ); 
        }

        assert( ibData.size() == *nIBs );
        assert( ibs.size() == *nIBs );

        // Vertex buffers
        auto nVBs = reinterpret_cast<const UINT*>( meshData + usedSize );
        usedSize += sizeof(UINT);
        if ( dataSize < usedSize )
            throw std::exception("End of file");

        if ( !*nVBs )
            throw std::exception("No vertex buffers found\n");

        struct VBData
        {
            size_t                                          nVerts;
            const VertexPositionNormalTangentColorTexture*  ptr;
            const VSD3DStarter::SkinningVertex*             skinPtr;
        };

        std::vector<VBData> vbData;
        vbData.reserve( *nVBs );
        for( UINT j = 0; j < *nVBs; ++j )
        {
            auto nVerts = reinterpret_cast<const UINT*>( meshData + usedSize );
            usedSize += sizeof(UINT);
            if ( dataSize < usedSize )
                throw std::exception("End of file");

            if ( !*nVerts )
                throw std::exception("Empty vertex buffer found\n");

            size_t vbBytes = sizeof(VertexPositionNormalTangentColorTexture) * (*(nVerts));

            auto verts = reinterpret_cast<const VertexPositionNormalTangentColorTexture*>( meshData + usedSize );
            usedSize += vbBytes;
            if ( dataSize < usedSize )
                throw std::exception("End of file");

            VBData vb;
            vb.nVerts = *nVerts;
            vb.ptr = verts;
            vb.skinPtr = nullptr;
            vbData.emplace_back( vb );
        }

        assert( vbData.size() == *nVBs );

        // Skinning vertex buffers
        auto nSkinVBs = reinterpret_cast<const UINT*>( meshData + usedSize );
        usedSize += sizeof(UINT);
        if ( dataSize < usedSize )
            throw std::exception("End of file");

        if ( *nSkinVBs )
        {
            if ( *nSkinVBs != *nVBs )
                throw std::exception("Number of VBs not equal to number of skin VBs");

            for( UINT j = 0; j < *nSkinVBs; ++j )
            {
                auto nVerts = reinterpret_cast<const UINT*>( meshData + usedSize );
                usedSize += sizeof(UINT);
                if ( dataSize < usedSize )
                    throw std::exception("End of file");

                if ( !*nVerts )
                    throw std::exception("Empty skinning vertex buffer found\n");

                if ( vbData[ j ].nVerts != *nVerts )
                    throw std::exception("Mismatched number of verts for skin VBs");
    
                size_t vbBytes = sizeof(VSD3DStarter::SkinningVertex) * (*(nVerts));

                auto verts = reinterpret_cast<const VSD3DStarter::SkinningVertex*>( meshData + usedSize );
                usedSize += vbBytes;
                if ( dataSize < usedSize )
                    throw std::exception("End of file");

                vbData[j].skinPtr = verts;
            }
        }

        // Extents
        auto extents = reinterpret_cast<const VSD3DStarter::MeshExtents*>( meshData + usedSize );
        usedSize += sizeof(VSD3DStarter::MeshExtents);
        if ( dataSize < usedSize )
            throw std::exception("End of file");

        mesh->boundingSphere.Center.x = extents->CenterX;
        mesh->boundingSphere.Center.y = extents->CenterY;
        mesh->boundingSphere.Center.z = extents->CenterZ;
        mesh->boundingSphere.Radius = extents->Radius;

        XMVECTOR min = XMVectorSet( extents->MinX, extents->MinY, extents->MinZ, 0.f );
        XMVECTOR max = XMVectorSet( extents->MaxX, extents->MaxY, extents->MaxZ, 0.f );
        BoundingBox::CreateFromPoints( mesh->boundingBox, min, max );

#if 0
        // Animation data
        if ( *bSkeleton )
        {
            // Bones
            auto nBones = reinterpret_cast<const UINT*>( meshData + usedSize );
            usedSize += sizeof(UINT);
            if ( dataSize < usedSize )
                throw std::exception("End of file");

            if ( !*nBones )
                throw std::exception("Animation bone data is missing\n");

            for( UINT j = 0; j < *nBones; ++j )
            {
                // Bone name
                nName = reinterpret_cast<const UINT*>( meshData + usedSize );
                usedSize += sizeof(UINT);
                if ( dataSize < usedSize )
                    throw std::exception("End of file");

                auto boneName = reinterpret_cast<const wchar_t*>( meshData + usedSize );

                usedSize += sizeof(wchar_t)*(*nName);
                if ( dataSize < usedSize )
                    throw std::exception("End of file");
                
                // TODO - What to do with bone name?
                boneName;

                // Bone settings
                auto bones = reinterpret_cast<const VSD3DStarter::Bone*>( meshData + usedSize );
                usedSize += sizeof(VSD3DStarter::Bone);
                if ( dataSize < usedSize )  
                    throw std::exception("End of file");

                // TODO - What to do with bone data?
                bones;
            }

            // Animation Clips
            auto nClips = reinterpret_cast<const UINT*>( meshData + usedSize );
            usedSize += sizeof(UINT);
            if ( dataSize < usedSize )
                throw std::exception("End of file");

            for( UINT j = 0; j < *nClips; ++j )
            {
                // Clip name
                nName = reinterpret_cast<const UINT*>( meshData + usedSize );
                usedSize += sizeof(UINT);
                if ( dataSize < usedSize )
                    throw std::exception("End of file");

                auto clipName = reinterpret_cast<const wchar_t*>( meshData + usedSize );

                usedSize += sizeof(wchar_t)*(*nName);
                if ( dataSize < usedSize )
                    throw std::exception("End of file");
                
                // TODO - What to do with clip name?
                clipName;

                auto clip = reinterpret_cast<const VSD3DStarter::Clip*>( meshData + usedSize );
                usedSize += sizeof(VSD3DStarter::Clip);
                if ( dataSize < usedSize )
                    throw std::exception("End of file");

                if ( !clip->keys )
                    throw std::exception("Keyframes missing in clip");

                auto keys = reinterpret_cast<const VSD3DStarter::Keyframe*>( meshData + usedSize );
                usedSize += sizeof(VSD3DStarter::Keyframe) * clip->keys;
                if ( dataSize < usedSize )  
                    throw std::exception("End of file");

                // TODO - What to do with keys and clip->StartTime, clip->EndTime?
                keys;
            }
        }
#else
        bSkeleton;
#endif

        bool enableSkinning = ( *nSkinVBs ) != 0;

        // Build vertex buffers
        std::vector<ComPtr<ID3D11Buffer>> vbs;
        vbs.resize( *nVBs );

        const size_t stride = enableSkinning ? sizeof(VertexPositionNormalTangentColorTextureSkinning)
                                             : sizeof(VertexPositionNormalTangentColorTexture);

        for( UINT j = 0; j < *nVBs; ++j )
        {
            size_t nVerts = vbData[ j ].nVerts;

            size_t bytes = stride * nVerts;

            D3D11_BUFFER_DESC desc = {0};
            desc.Usage = D3D11_USAGE_DEFAULT;
            desc.ByteWidth = static_cast<UINT>( bytes );
            desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
            
            if ( fxFactoryDGSL && !enableSkinning )
            {
                // Can use CMO vertex data directly
                D3D11_SUBRESOURCE_DATA initData = {0};
                initData.pSysMem = vbData[j].ptr;

                ThrowIfFailed(
                    d3dDevice->CreateBuffer( &desc, &initData, &vbs[j] )
                    );
            }
            else
            {
                std::unique_ptr<uint8_t> temp( new uint8_t[ bytes + ( sizeof(UINT) * nVerts ) ] );

                auto visited = reinterpret_cast<UINT*>( temp.get() + bytes );
                memset( visited, 0xff, sizeof(UINT) * nVerts );

                assert( vbData[j].ptr != 0 );

                if ( enableSkinning )
                {
                    // Combine CMO multi-stream data into a single stream
                    auto skinptr = vbData[j].skinPtr;
                    assert( skinptr != 0 );

                    uint8_t* ptr = temp.get();

                    auto sptr = vbData[j].ptr;

                    for( size_t v = 0; v < nVerts; ++v )
                    {
                        *reinterpret_cast<VertexPositionNormalTangentColorTexture*>( ptr ) = *sptr;
                        ++sptr;

                        auto skinv = reinterpret_cast<VertexPositionNormalTangentColorTextureSkinning*>( ptr );
                        skinv->SetBlendIndices( *reinterpret_cast<const XMUINT4*>( skinptr->boneIndex ) );
                        skinv->SetBlendWeights( *reinterpret_cast<const XMFLOAT4*>( skinptr->boneWeight ) );

                        ptr += stride;
                    }
                }
                else
                {
                    memcpy( temp.get(), vbData[j].ptr, bytes );
                }

                if ( !fxFactoryDGSL )
                {
                    // Need to fix up VB tex coords for UV transform which is not supported by basic effects
                    for( UINT k = 0; k < *nSubmesh; ++k )
                    {
                        auto& sm = subMesh[ k ];

                        if ( sm.VertexBufferIndex != j )
                            continue;

                        if ( (sm.IndexBufferIndex >= *nIBs)
                             || (sm.MaterialIndex >= *nMats) )
                             throw std::exception("Invalid submesh found\n");

                        XMMATRIX uvTransform = XMLoadFloat4x4( &materials[ sm.MaterialIndex ].pMaterial->UVTransform );

                        auto ib = ibData[ sm.IndexBufferIndex ].ptr;

                        size_t count = ibData[ sm.IndexBufferIndex ].nIndices;

                        for( size_t q = 0; q < count; ++q )
                        {
                            size_t v = ib[ q ];

                            if ( v >= nVerts )
                                throw std::exception("Invalid index found\n");

                            auto verts = reinterpret_cast<VertexPositionNormalTangentColorTexture*>( temp.get() + ( v * stride ) );
                            if ( visited[v] == UINT(-1) )
                            {
                                visited[v] = sm.MaterialIndex;

                                XMVECTOR t = XMLoadFloat2( &verts->textureCoordinate );

                                t = XMVectorSelect( g_XMIdentityR3, t, g_XMSelect1110 );

                                t = XMVector4Transform( t, uvTransform );

                                XMStoreFloat2( &verts->textureCoordinate, t );
                            }
                            else if ( visited[v] != sm.MaterialIndex )
                            {
#ifdef _DEBUG
                                XMMATRIX uv2 = XMLoadFloat4x4( &materials[ visited[v] ].pMaterial->UVTransform );

                                if ( XMVector4NotEqual( uvTransform.r[0], uv2.r[0] )
                                     || XMVector4NotEqual( uvTransform.r[1], uv2.r[1] )
                                     || XMVector4NotEqual( uvTransform.r[2], uv2.r[2] )
                                     || XMVector4NotEqual( uvTransform.r[3], uv2.r[3] ) )
                                {
                                    wchar_t buff[1024];
                                    swprintf_s( buff, L"WARNING: %s - mismatched UV transforms for the same vertex; texture coordinates may not be correct\n", mesh->name.c_str() );
                                    OutputDebugString( buff );
                                }
#endif
                            }
                        }
                    }
                }

                // Create vertex buffer from temporary buffer
                D3D11_SUBRESOURCE_DATA initData = {0};
                initData.pSysMem = temp.get();

                ThrowIfFailed(
                    d3dDevice->CreateBuffer( &desc, &initData, &vbs[j] )
                    );
            }

            SetDebugObjectName( vbs[j].Get(), "ModelCMO" ); 
        }

        assert( vbs.size() == *nVBs );
        
        // Create Effects
        for( UINT j = 0; j < *nMats; ++j )
        {
            auto& m = materials[ j ];

            if ( fxFactoryDGSL )
            {
                DGSLEffectFactory::DGSLEffectInfo info;
                info.name = m.name.c_str();
                info.specularPower = m.pMaterial->SpecularPower;
                info.perVertexColor = true;
                info.enableSkinning = enableSkinning;
                info.alpha = m.pMaterial->Diffuse.w;
                info.ambientColor = XMFLOAT3( m.pMaterial->Ambient.x, m.pMaterial->Ambient.y, m.pMaterial->Ambient.z );
                info.diffuseColor = XMFLOAT3( m.pMaterial->Diffuse.x, m.pMaterial->Diffuse.y, m.pMaterial->Diffuse.z );
                info.specularColor = XMFLOAT3( m.pMaterial->Specular.x, m.pMaterial->Specular.y, m.pMaterial->Specular.z );
                info.emissiveColor = XMFLOAT3( m.pMaterial->Emissive.x, m.pMaterial->Emissive.y, m.pMaterial->Emissive.z );
                info.texture = m.texture[0].c_str();
                info.pixelShader = m.pixelShader.c_str();
                
                for( int i = 0; i < 7; ++i )
                {
                    info.textures[i] = m.texture[ i+1 ].empty() ? nullptr : m.texture[i].c_str();
                }

                m.effect = fxFactoryDGSL->CreateDGSLEffect( info, nullptr );

                auto dgslEffect = reinterpret_cast<DGSLEffect*>( m.effect.get() );
                dgslEffect->SetUVTransform( XMLoadFloat4x4( &m.pMaterial->UVTransform ) );
            }
            else
            {
                EffectFactory::EffectInfo info;
                info.name = m.name.c_str();
                info.specularPower = m.pMaterial->SpecularPower;
                info.perVertexColor = true;
                info.enableSkinning = enableSkinning;
                info.alpha = m.pMaterial->Diffuse.w;
                info.ambientColor = XMFLOAT3( m.pMaterial->Ambient.x, m.pMaterial->Ambient.y, m.pMaterial->Ambient.z );
                info.diffuseColor = XMFLOAT3( m.pMaterial->Diffuse.x, m.pMaterial->Diffuse.y, m.pMaterial->Diffuse.z );
                info.specularColor = XMFLOAT3( m.pMaterial->Specular.x, m.pMaterial->Specular.y, m.pMaterial->Specular.z );
                info.emissiveColor = XMFLOAT3( m.pMaterial->Emissive.x, m.pMaterial->Emissive.y, m.pMaterial->Emissive.z );
                info.texture = m.texture[0].c_str();

                m.effect = fxFactory.CreateEffect( info, nullptr );
            }

            CreateInputLayout( d3dDevice, m.effect.get(), &m.il, enableSkinning );
        }

        // Build mesh parts
        for( UINT j = 0; j < *nSubmesh; ++j )
        {
            auto& sm = subMesh[j];

            if ( (sm.IndexBufferIndex >= *nIBs)
                 || (sm.VertexBufferIndex >= *nVBs)
                 || (sm.MaterialIndex >= *nMats) )
                 throw std::exception("Invalid submesh found\n");

            auto& mat = materials[ sm.MaterialIndex ];

            auto part = new ModelMeshPart();

            if ( mat.pMaterial->Diffuse.w < 1 )
                part->isAlpha = true;

            part->indexCount = sm.PrimCount * 3;
            part->startIndex = sm.StartIndex;
            part->vertexStride = static_cast<UINT>( stride );
            part->inputLayout = mat.il;
            part->indexBuffer = ibs[ sm.IndexBufferIndex ];
            part->vertexBuffer = vbs[ sm.VertexBufferIndex ];
            part->effect = mat.effect;
            part->vbDecl = enableSkinning ? g_vbdeclSkinning : g_vbdecl;

            mesh->meshParts.emplace_back( part );
        }

        model->meshes.emplace_back( mesh );
    }

    return model;
}
//--------------------------------------------------------------------------------------
_Use_decl_annotations_
std::unique_ptr<Model> DirectX::Model::CreateFromCMO( ID3D11Device* d3dDevice, const uint8_t* meshData, size_t dataSize, IEffectFactory& fxFactory, bool ccw, bool pmalpha )
{
    if ( !InitOnceExecuteOnce( &g_InitOnce, InitializeDecl, nullptr, nullptr ) )
        throw std::exception("One-time initialization failed");
    
    if ( !d3dDevice || !meshData )
        throw std::exception("Device and meshData cannot be null");

    // Meshes
    auto nMesh = reinterpret_cast<const UINT*>( meshData );
    size_t usedSize = sizeof(UINT);
    if ( dataSize < usedSize )
        throw std::exception("End of file");

    if ( !*nMesh )
        throw std::exception("No meshes found");

    std::unique_ptr<Model> model(new Model());

    for( UINT meshIndex = 0; meshIndex < *nMesh; ++meshIndex )
    {
        // Mesh name
        auto nName = reinterpret_cast<const UINT*>( meshData + usedSize );
        usedSize += sizeof(UINT);
        if ( dataSize < usedSize )
            throw std::exception("End of file");

        auto meshName = reinterpret_cast<const wchar_t*>( meshData + usedSize );

        usedSize += sizeof(wchar_t)*(*nName);
        if ( dataSize < usedSize )
            throw std::exception("End of file");

        auto mesh = std::make_shared<ModelMesh>();
        mesh->name.assign( meshName, *nName );
        mesh->ccw = ccw;
        mesh->pmalpha = pmalpha;

        // Materials
        auto nMats = reinterpret_cast<const UINT*>( meshData + usedSize );
        usedSize += sizeof(UINT);
        if ( dataSize < usedSize )
            throw std::exception("End of file");

        std::vector<MaterialRecordCMO> materials;
        materials.reserve( *nMats );
        for( UINT j = 0; j < *nMats; ++j )
        {
            MaterialRecordCMO m;

            // Material name
            nName = reinterpret_cast<const UINT*>( meshData + usedSize );
            usedSize += sizeof(UINT);
            if ( dataSize < usedSize )
                throw std::exception("End of file");

            auto matName = reinterpret_cast<const wchar_t*>( meshData + usedSize );

            usedSize += sizeof(wchar_t)*(*nName);
            if ( dataSize < usedSize )
                throw std::exception("End of file");

            m.name.assign( matName, *nName );

            // Material settings
            auto matSetting = reinterpret_cast<const VSD3DStarter::Material*>( meshData + usedSize );
            usedSize += sizeof(VSD3DStarter::Material);
            if ( dataSize < usedSize )
                throw std::exception("End of file");

            m.pMaterial = matSetting;

            // Pixel shader name
            nName = reinterpret_cast<const UINT*>( meshData + usedSize );
            usedSize += sizeof(UINT);
            if ( dataSize < usedSize )
                throw std::exception("End of file");

            auto psName = reinterpret_cast<const wchar_t*>( meshData + usedSize );

            usedSize += sizeof(wchar_t)*(*nName);
            if ( dataSize < usedSize )
                throw std::exception("End of file");

            m.pixelShader.assign( psName, *nName );

            for( UINT t = 0; t < VSD3DStarter::MAX_TEXTURE; ++t )
            {
                nName = reinterpret_cast<const UINT*>( meshData + usedSize );
                usedSize += sizeof(UINT);
                if ( dataSize < usedSize )
                    throw std::exception("End of file");

                auto txtName = reinterpret_cast<const wchar_t*>( meshData + usedSize );

                usedSize += sizeof(wchar_t)*(*nName);
                if ( dataSize < usedSize )
                    throw std::exception("End of file");

                m.texture[t].assign( txtName, *nName );
            }

            EffectFactory::EffectInfo info;
            info.name = m.name.c_str();
            info.specularPower = m.pMaterial->SpecularPower;
            info.perVertexColor = true;
            info.alpha = m.pMaterial->Diffuse.w;
            info.ambientColor = XMFLOAT3( m.pMaterial->Ambient.x, m.pMaterial->Ambient.y, m.pMaterial->Ambient.z );
            info.diffuseColor = XMFLOAT3( m.pMaterial->Diffuse.x, m.pMaterial->Diffuse.y, m.pMaterial->Diffuse.z );
            info.specularColor = XMFLOAT3( m.pMaterial->Specular.x, m.pMaterial->Specular.y, m.pMaterial->Specular.z );
            info.emissiveColor = XMFLOAT3( m.pMaterial->Emissive.x, m.pMaterial->Emissive.y, m.pMaterial->Emissive.z );
            info.texture = m.texture[0].c_str();

            m.effect = fxFactory.CreateEffect( info, nullptr );

            CreateInputLayout( d3dDevice, m.effect.get(), &m.il );

            materials.emplace_back( m );
        }

        // Skeletal data?
        auto bSkeleton = reinterpret_cast<const BYTE*>( meshData + usedSize );
        usedSize += sizeof(BYTE);
        if ( dataSize < usedSize )
            throw std::exception("End of file");

        // Submeshes
        auto nSubmesh = reinterpret_cast<const UINT*>( meshData + usedSize );
        usedSize += sizeof(UINT);
        if ( dataSize < usedSize )
            throw std::exception("End of file");

        if ( !*nSubmesh )
            throw std::exception("No submeshes found\n");

        auto subMesh = reinterpret_cast<const VSD3DStarter::SubMesh*>( meshData + usedSize );
        usedSize += sizeof(VSD3DStarter::SubMesh) * (*nSubmesh);
        if ( dataSize < usedSize )
            throw std::exception("End of file");

        // Index buffers
        auto nIBs = reinterpret_cast<const UINT*>( meshData + usedSize );
        usedSize += sizeof(UINT);
        if ( dataSize < usedSize )
            throw std::exception("End of file");

        if ( !*nIBs )
            throw std::exception("No index buffers found\n");

        std::vector<ComPtr<ID3D11Buffer>> ibs;
        ibs.resize( *nIBs );
        for( UINT j = 0; j < *nIBs; ++j )
        {
            auto nIndexes = reinterpret_cast<const UINT*>( meshData + usedSize );
            usedSize += sizeof(UINT);
            if ( dataSize < usedSize )
                throw std::exception("End of file");

            if ( !*nIndexes )
                throw std::exception("Empty index buffer found\n");

            size_t ibBytes = sizeof(USHORT) * (*(nIndexes));

            auto indexes = reinterpret_cast<const USHORT*>( meshData + usedSize );
            usedSize += ibBytes;
            if ( dataSize < usedSize )
                throw std::exception("End of file");

            D3D11_BUFFER_DESC desc = {0};
            desc.Usage = D3D11_USAGE_DEFAULT;
            desc.ByteWidth = static_cast<UINT>( ibBytes );
            desc.BindFlags = D3D11_BIND_INDEX_BUFFER;

            D3D11_SUBRESOURCE_DATA initData = {0};
            initData.pSysMem = indexes;

            ThrowIfFailed(
                d3dDevice->CreateBuffer( &desc, &initData, &ibs[j] )
                );

            SetDebugObjectName( ibs[j].Get(), "ModelCMO" ); 
        }

        // Vertex buffers
        auto nVBs = reinterpret_cast<const UINT*>( meshData + usedSize );
        usedSize += sizeof(UINT);
        if ( dataSize < usedSize )
            throw std::exception("End of file");

        if ( !*nVBs )
            throw std::exception("No vertex buffers found\n");

        std::vector<ComPtr<ID3D11Buffer>> vbs;
        vbs.resize( *nVBs );
        for( UINT j = 0; j < *nVBs; ++j )
        {
            auto nVerts = reinterpret_cast<const UINT*>( meshData + usedSize );
            usedSize += sizeof(UINT);
            if ( dataSize < usedSize )
                throw std::exception("End of file");

            if ( !*nVerts )
                throw std::exception("Empty vertex buffer found\n");

            size_t vbBytes = sizeof(VSD3DStarter::Vertex) * (*(nVerts));

            auto verts = reinterpret_cast<const VSD3DStarter::Vertex*>( meshData + usedSize );
            usedSize += vbBytes;
            if ( dataSize < usedSize )
                throw std::exception("End of file");

            D3D11_BUFFER_DESC desc = {0};
            desc.Usage = D3D11_USAGE_DEFAULT;
            desc.ByteWidth = static_cast<UINT>( vbBytes );
            desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;

            D3D11_SUBRESOURCE_DATA initData = {0};
            initData.pSysMem = verts;

            ThrowIfFailed(
                d3dDevice->CreateBuffer( &desc, &initData, &vbs[j] )
                );

            SetDebugObjectName( vbs[j].Get(), "ModelCMO" ); 
        }

        // Skinning vertex buffers
        auto nSkinVBs = reinterpret_cast<const UINT*>( meshData + usedSize );
        usedSize += sizeof(UINT);
        if ( dataSize < usedSize )
            throw std::exception("End of file");

        for( UINT j = 0; j < *nSkinVBs; ++j )
        {
            auto nVerts = reinterpret_cast<const UINT*>( meshData + usedSize );
            usedSize += sizeof(UINT);
            if ( dataSize < usedSize )
                throw std::exception("End of file");

            if ( !*nVerts )
                throw std::exception("Empty skinning vertex buffer found\n");

            size_t vbBytes = sizeof(VSD3DStarter::SkinningVertex) * (*(nVerts));

            auto verts = reinterpret_cast<const VSD3DStarter::SkinningVertex*>( meshData + usedSize );
            usedSize += vbBytes;
            if ( dataSize < usedSize )
                throw std::exception("End of file");

            // TODO - What to do with skinning data?
            verts;
        }

        // Extents
        auto extents = reinterpret_cast<const VSD3DStarter::MeshExtents*>( meshData + usedSize );
        usedSize += sizeof(VSD3DStarter::MeshExtents);
        if ( dataSize < usedSize )
            throw std::exception("End of file");

        mesh->boundingSphere.Center.x = extents->CenterX;
        mesh->boundingSphere.Center.y = extents->CenterY;
        mesh->boundingSphere.Center.z = extents->CenterZ;
        mesh->boundingSphere.Radius = extents->Radius;

        XMVECTOR min = XMVectorSet( extents->MinX, extents->MinY, extents->MinZ, 0.f );
        XMVECTOR max = XMVectorSet( extents->MaxX, extents->MaxY, extents->MaxZ, 0.f );
        BoundingBox::CreateFromPoints( mesh->boundingBox, min, max );

#if 0
        // Animation data
        if ( *bSkeleton )
        {
            // Bones
            auto nBones = reinterpret_cast<const UINT*>( meshData + usedSize );
            usedSize += sizeof(UINT);
            if ( dataSize < usedSize )
                throw std::exception("End of file");

            if ( !*nBones )
                throw std::exception("Animation bone data is missing\n");

            for( UINT j = 0; j < *nBones; ++j )
            {
                // Bone name
                nName = reinterpret_cast<const UINT*>( meshData + usedSize );
                usedSize += sizeof(UINT);
                if ( dataSize < usedSize )
                    throw std::exception("End of file");

                auto boneName = reinterpret_cast<const wchar_t*>( meshData + usedSize );

                usedSize += sizeof(wchar_t)*(*nName);
                if ( dataSize < usedSize )
                    throw std::exception("End of file");
                
                // TODO - What to do with bone name?
                boneName;

                // Bone settings
                auto bones = reinterpret_cast<const VSD3DStarter::Bone*>( meshData + usedSize );
                usedSize += sizeof(VSD3DStarter::Bone);
                if ( dataSize < usedSize )  
                    throw std::exception("End of file");

                // TODO - What to do with bone data?
                bones;
            }

            // Animation Clips
            auto nClips = reinterpret_cast<const UINT*>( meshData + usedSize );
            usedSize += sizeof(UINT);
            if ( dataSize < usedSize )
                throw std::exception("End of file");

            for( UINT j = 0; j < *nClips; ++j )
            {
                // Clip name
                nName = reinterpret_cast<const UINT*>( meshData + usedSize );
                usedSize += sizeof(UINT);
                if ( dataSize < usedSize )
                    throw std::exception("End of file");

                auto clipName = reinterpret_cast<const wchar_t*>( meshData + usedSize );

                usedSize += sizeof(wchar_t)*(*nName);
                if ( dataSize < usedSize )
                    throw std::exception("End of file");
                
                // TODO - What to do with clip name?
                clipName;

                auto clip = reinterpret_cast<const VSD3DStarter::Clip*>( meshData + usedSize );
                usedSize += sizeof(VSD3DStarter::Clip);
                if ( dataSize < usedSize )
                    throw std::exception("End of file");

                if ( !clip->keys )
                    throw std::exception("Keyframes missing in clip");

                auto keys = reinterpret_cast<const VSD3DStarter::Keyframe*>( meshData + usedSize );
                usedSize += sizeof(VSD3DStarter::Keyframe) * clip->keys;
                if ( dataSize < usedSize )  
                    throw std::exception("End of file");

                // TODO - What to do with keys and clip->StartTime, clip->EndTime?
                keys;
            }
        }
#else
        bSkeleton;
#endif

        // Build mesh parts
        for( UINT j = 0; j < *nSubmesh; ++j )
        {
            auto& sm = subMesh[j];

            if ( (sm.IndexBufferIndex >= *nIBs)
                 || (sm.VertexBufferIndex >= *nVBs)
                 || (sm.MaterialIndex >= *nMats) )
                 throw std::exception("Invalid submesh found\n");

            auto& mat = materials[ sm.MaterialIndex ];

            auto part = new ModelMeshPart();

            if ( mat.pMaterial->Diffuse.w < 1 )
                part->isAlpha = true;

            part->indexCount = sm.PrimCount * 3;
            part->startIndex = sm.StartIndex;
            part->vertexStride = sizeof(VSD3DStarter::Vertex);
            part->inputLayout = mat.il;
            part->indexBuffer = ibs[ sm.IndexBufferIndex ];
            part->vertexBuffer = vbs[ sm.VertexBufferIndex ];
            part->effect = mat.effect;
            part->vbDecl = g_vbdecl;

            mesh->meshParts.emplace_back( part );
        }

        model->meshes.emplace_back( mesh );
    }

    return model;
}