Win10 Create Model

Windows10 DirectX3D で Normal Model を生成する Class を作成します。

前田稔(Maeda Minoru)の超初心者のプログラム入門

プログラムの説明

  1. DirectX9 では自動的にモデルを生成する関数が備わっていました。
    所が DirectX Store Program ではこれが無くなりました。
    そこで、これに変わる Normal Model を生成する Class CreateModel を作成することにします。
    モデルを生成する簡単な Class は Win10 Create Color を参照して下さい。
  2. Win10 Template を参照して DirectX 11 および XAML アプリ(ユニバーサル Windows)を生成して下さい。
    生成したアプリは「頂点+カラー」のモデルを描画するプロジェクトです。
    「頂点+法線」のモデルをマウスの操作で自由に回転出来るようにして下さい。
    詳細は Win10 Norm Cone を参照して下さい。
    もしこのプロジェクトが完成しているなら、フォルダーごとコピーして使うことも出来ます。
  3. Content\ のフォルダーに Class CreateModel のファイルを格納してプロジェクトに追加します。
    ソースコードはこの後に掲載します。
    ファイル 説明
    Content\CreateModel.h CreateModel の Header File
    Content\CreateModel.cpp CreateModel の Program File
  4. Sample3DSceneRenderer.h で Header File を組み込んで、Class の領域を定義します。
    #include "CreateModel.h"
    
        CreateModel     *m_CreateModel;
    
  5. Sample3DSceneRenderer.cpp でモデルを生成して描画します。
    new CreateModel() で CreateModel を生成して m_CreateModel->CreateCube() でモデルを取得します。
    void Sample3DSceneRenderer::CreateDeviceDependentResources()
    {
        // シェーダーを非同期で読み込みます。
        auto loadVSTask = DX::ReadDataAsync(L"SampleVertexShader.cso");
        auto loadPSTask = DX::ReadDataAsync(L"SamplePixelShader.cso");
    
        // 頂点シェーダー ファイルを読み込んだ後、シェーダーと入力レイアウトを作成します。
        auto createVSTask = loadVSTask.then([this](const std::vector<byte>& fileData) {
            DX::ThrowIfFailed(
                m_deviceResources->GetD3DDevice()->CreateVertexShader(
                    &fileData[0],
                    fileData.size(),
                    nullptr,
                    &m_vertexShader
                    )
                );
    
            static const D3D11_INPUT_ELEMENT_DESC vertexDesc [] =
            {
                { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
                //{ "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
                { "NORMAL",0,DXGI_FORMAT_R32G32B32_FLOAT,0,12,D3D11_INPUT_PER_VERTEX_DATA,0 },
            };
    
            DX::ThrowIfFailed(
                m_deviceResources->GetD3DDevice()->CreateInputLayout(
                    vertexDesc,
                    ARRAYSIZE(vertexDesc),
                    &fileData[0],
                    fileData.size(),
                    &m_inputLayout
                    )
                );
        });
    
        // ピクセル シェーダー ファイルを読み込んだ後、シェーダーと定数バッファーを作成します。
        auto createPSTask = loadPSTask.then([this](const std::vector<byte>& fileData) {
            DX::ThrowIfFailed(
                m_deviceResources->GetD3DDevice()->CreatePixelShader(
                    &fileData[0],
                    fileData.size(),
                    nullptr,
                    &m_pixelShader
                    )
                );
    
            CD3D11_BUFFER_DESC constantBufferDesc(sizeof(ModelViewProjectionConstantBuffer) , D3D11_BIND_CONSTANT_BUFFER);
            DX::ThrowIfFailed(
                m_deviceResources->GetD3DDevice()->CreateBuffer(
                    &constantBufferDesc,
                    nullptr,
                    &m_constantBuffer
                    )
                );
        });
    
        // 両方のシェーダーの読み込みが完了したら、メッシュを作成します。
        auto createCubeTask = (createPSTask && createVSTask).then([this]() {
            m_CreateModel = new CreateModel(m_deviceResources);
            m_CreateModel->CreateCube(&m_vertexBuffer, &m_indexBuffer, &m_indexCount); 
            //m_CreateModel->CreateCone(19, &m_vertexBuffer, &m_indexBuffer, &m_indexCount);
        });
    
        // モデルが読み込まれたら、オブジェクトを描画する準備が完了します。
        createCubeTask.then([this]()
        {   m_loadingComplete = true;  });
    }
    
  6. CreateModel.h のソースコードです。
    // Create Normal Model  前田 稔
    #pragma once
    
    #include "..\Common\DeviceResources.h"
    #define  KAKU  21  // (多角錐の数は最大20まで)
    
    struct VertexNorm
    {
        DirectX::XMFLOAT3 pos;
        DirectX::XMFLOAT3 norm;
    };
    
    class CreateModel
    {
    public:
        CreateModel(std::shared_ptr<DX::DeviceResources>& deviceResources);
        void CreateCube(
            _Out_ ID3D11Buffer **vertexBuffer,
            _Out_ ID3D11Buffer **indexBuffer,
            _Out_ uint32 *indexCount);
    
        void Debug(uint16 *ary, uint32 siz);
        void DebugVert(VertexNorm *Vert, uint32 siz);
        void DebugVert(VertexNorm Vert, uint32 num);
        void DebugVert(VertexNorm *Vert, uint16 *ary, uint32 siz);
    
    private:
        std::shared_ptr<DX::DeviceResources> m_deviceResources;
    };
    
  7. CreateModel.cpp のソースコードです。
    頂点座標が同じでも法線ベクトルが異なれば別のデータとして定義します。
    cubeIndices で頂点データを組み合わせて三角ポリゴンを構成します。
    // Create Normal Model  前田 稔
    #pragma once
    
    #include "pch.h"
    #include "..\Common\DirectXHelper.h"
    #include "CreateModel.h"
    using namespace DirectX;
    
    void CreateModel::Debug(uint16 *ary, uint32 siz)
    {
        Platform::String ^str;
        str = L"Array[";
        str += siz.ToString();
        str += L"]\r\n";
        OutputDebugString(str->Data());
        for (uint32 i = 0; i<siz; i++)
        {
            str = ary[i].ToString();
            str += "\r\n";
            OutputDebugString(str->Data());
        }
    }
    void CreateModel::DebugVert(VertexNorm *Vert, uint32 siz)
    {
        for (uint32 i = 0; i<siz; i++)
        {
            DebugVert(Vert[i], i);
        }
    }
    void CreateModel::DebugVert(VertexNorm Vert, uint32 num)
    {
        Platform::String ^str;
        str = L"Vert-";
        str += num.ToString();
        str += L"( ";
        str += Vert.pos.x.ToString();
        str += ",";
        str += Vert.pos.y.ToString();
        str += ",";
        str += Vert.pos.z.ToString();
        str += ") (";
        str += Vert.norm.x.ToString();
        str += ",";
        str += Vert.norm.y.ToString();
        str += ",";
        str += Vert.norm.z.ToString();
        str += ")\r\n";
        OutputDebugString(str->Data());
    }
    void CreateModel::DebugVert(VertexNorm *Vert, uint16 *ary, uint32 siz)
    {
        for(uint32 i=0; i<siz; i++)
        {   if (i%3==0) OutputDebugString(L"(");
            DebugVert(Vert[ary[i]], i);
            if (i%3==2) OutputDebugString(L")\r\n");
        }
    }
    
    CreateModel::CreateModel(std::shared_ptr<DX::DeviceResources>& deviceResources) :
        m_deviceResources(deviceResources)
    {
    }
    
    void CreateModel::CreateCube(
        _Out_ ID3D11Buffer **vertexBuffer,
        _Out_ ID3D11Buffer **indexBuffer,
        _Out_ uint32 *indexCount)
    {
        static const VertexNorm cubeVertices[] = 
        {
            { XMFLOAT3(-0.5f, 0.5f, -0.5f), XMFLOAT3(0.0f, 1.0f, 0.0f) },   // +Y (top face)
            { XMFLOAT3( 0.5f, 0.5f, -0.5f), XMFLOAT3(0.0f, 1.0f, 0.0f) },
            { XMFLOAT3( 0.5f, 0.5f,  0.5f), XMFLOAT3(0.0f, 1.0f, 0.0f) },
            { XMFLOAT3(-0.5f, 0.5f,  0.5f), XMFLOAT3(0.0f, 1.0f, 0.0f) },
    
            { XMFLOAT3(-0.5f, -0.5f,  0.5f), XMFLOAT3(0.0f, -1.0f, 0.0f) }, // -Y (bottom face)
            { XMFLOAT3( 0.5f, -0.5f,  0.5f), XMFLOAT3(0.0f, -1.0f, 0.0f) },
            { XMFLOAT3( 0.5f, -0.5f, -0.5f), XMFLOAT3(0.0f, -1.0f, 0.0f) },
            { XMFLOAT3(-0.5f, -0.5f, -0.5f), XMFLOAT3(0.0f, -1.0f, 0.0f) },
    
            { XMFLOAT3(0.5f,  0.5f,  0.5f), XMFLOAT3(1.0f, 0.0f, 0.0f) },   // +X (right face)
            { XMFLOAT3(0.5f,  0.5f, -0.5f), XMFLOAT3(1.0f, 0.0f, 0.0f) },
            { XMFLOAT3(0.5f, -0.5f, -0.5f), XMFLOAT3(1.0f, 0.0f, 0.0f) },
            { XMFLOAT3(0.5f, -0.5f,  0.5f), XMFLOAT3(1.0f, 0.0f, 0.0f) },
    
            { XMFLOAT3(-0.5f,  0.5f, -0.5f), XMFLOAT3(-1.0f, 0.0f, 0.0f) }, // -X (left face)
            { XMFLOAT3(-0.5f,  0.5f,  0.5f), XMFLOAT3(-1.0f, 0.0f, 0.0f) },
            { XMFLOAT3(-0.5f, -0.5f,  0.5f), XMFLOAT3(-1.0f, 0.0f, 0.0f) },
            { XMFLOAT3(-0.5f, -0.5f, -0.5f), XMFLOAT3(-1.0f, 0.0f, 0.0f) },
    
            { XMFLOAT3(-0.5f,  0.5f, 0.5f), XMFLOAT3(0.0f, 0.0f, 1.0f) },   // +Z (front face)
            { XMFLOAT3( 0.5f,  0.5f, 0.5f), XMFLOAT3(0.0f, 0.0f, 1.0f) },
            { XMFLOAT3( 0.5f, -0.5f, 0.5f), XMFLOAT3(0.0f, 0.0f, 1.0f) },
            { XMFLOAT3(-0.5f, -0.5f, 0.5f), XMFLOAT3(0.0f, 0.0f, 1.0f) },
    
            { XMFLOAT3( 0.5f,  0.5f, -0.5f), XMFLOAT3(0.0f, 0.0f, -1.0f) }, // -Z (back face)
            { XMFLOAT3(-0.5f,  0.5f, -0.5f), XMFLOAT3(0.0f, 0.0f, -1.0f) },
            { XMFLOAT3(-0.5f, -0.5f, -0.5f), XMFLOAT3(0.0f, 0.0f, -1.0f) },
            { XMFLOAT3( 0.5f, -0.5f, -0.5f), XMFLOAT3(0.0f, 0.0f, -1.0f) },
        };
    
        D3D11_SUBRESOURCE_DATA vertexBufferData = {0};
        vertexBufferData.pSysMem = cubeVertices;
        vertexBufferData.SysMemPitch = 0;
        vertexBufferData.SysMemSlicePitch = 0;
        CD3D11_BUFFER_DESC vertexBufferDesc(sizeof(cubeVertices), D3D11_BIND_VERTEX_BUFFER);
        DX::ThrowIfFailed(
            m_deviceResources->GetD3DDevice()->CreateBuffer(
                &vertexBufferDesc,
                &vertexBufferData,
                vertexBuffer
            ));
    
        static const unsigned short cubeIndices [] =
        {
            0, 1, 2,      0, 2, 3,
            4, 5, 6,      4, 6, 7,
            8, 9, 10,     8, 10, 11,
            12, 13, 14,   12, 14, 15,
            16, 17, 18,   16, 18, 19,
            20, 21, 22,   20, 22, 23
        };
    
        *indexCount = ARRAYSIZE(cubeIndices);
    
        D3D11_SUBRESOURCE_DATA indexBufferData = {0};
        indexBufferData.pSysMem = cubeIndices;
        indexBufferData.SysMemPitch = 0;
        indexBufferData.SysMemSlicePitch = 0;
        CD3D11_BUFFER_DESC indexBufferDesc(sizeof(cubeIndices), D3D11_BIND_INDEX_BUFFER);
        DX::ThrowIfFailed(
            m_deviceResources->GetD3DDevice()->CreateBuffer(
                &indexBufferDesc,
                &indexBufferData,
                indexBuffer));
    }
    
  8. 今は CreateCube() しか定義していませんが、同じ要領で他のモデルも加えて下さい。
    プログラムのデバッグに使用する Debug() 関数も組み込んでいます。
    DirectX 3Dのプログラムではモデルの作成(X-FILE の解析)が面倒なのですが、Class CreateModel を使うと非常に簡単です。
    テクスチャを貼り付けるモデルの生成は Win10 BasicShapes を参照して下さい。

CreateCone 関数を追加

  1. 多角錐を生成する CreateCone() 関数を追加します。
    CreateModel.h にプロトタイプ宣言を追加して下さい。
    第一パラメータ kaku が多角錐の角の数で、このパラメータを変えることにより色々なモデルを生成することが出来ます。
    4を渡すと3角錐(3面体)が生成されます。
    5を渡すとピラミッド(4面体)が生成されます。
    19を渡すと角が取れて円錐のようになります。
        void CreateModel::CreateCone(
            _In_  uint32 kaku,
            _Out_ ID3D11Buffer **vertexBuffer,
            _Out_ ID3D11Buffer **indexBuffer,
            _Out_ uint32 *indexCount);
    
        // 両方のシェーダーの読み込みが完了したら、メッシュを作成します。
        auto createCubeTask = (createPSTask && createVSTask).then([this]() {
            m_CreateModel = new CreateModel(m_deviceResources);
            m_CreateModel->CreateCone(19, &m_vertexBuffer, &m_indexBuffer, &m_indexCount); 
        });
        // モデルが読み込まれたら、オブジェクトを描画する準備が完了します。
        createCubeTask.then([this]()
        {   m_loadingComplete = true;  });
    }
    
  2. 多角錐を生成する CreateCone() 関数本体です。
    コーンの生成は Win10 Norm Cone を参照して下さい。
    void CreateModel::CreateCone(
        _In_  uint32 kaku,
        _Out_ ID3D11Buffer **vertexBuffer,
        _Out_ ID3D11Buffer **indexBuffer,
        _Out_ uint32 *indexCount)
    {
        static VertexNorm coneVertex[KAKU * 4];
        // 辺の座標, 頂点座標, 蓋の座標, 蓋の中心
        for (uint32 slice = 0; slice<kaku; slice++)
        {
            float v = (float)slice / (float)(kaku - 1);
            float theta = v * 3.14f * 2;
            coneVertex[slice * 4 + 0].pos = XMFLOAT3(sinf(theta), -0.7f, cosf(theta));
            coneVertex[slice * 4 + 1].pos = XMFLOAT3(0.0f, 0.7f, 0.0f);
            coneVertex[slice * 4 + 2].pos = XMFLOAT3(sinf(theta), -0.7f, cosf(theta));
            coneVertex[slice * 4 + 3].pos = XMFLOAT3(0.0f, -0.7f, 0.0f);
            coneVertex[slice * 4 + 0].norm = XMFLOAT3(sinf(theta), 0.7f, cosf(theta));
            coneVertex[slice * 4 + 1].norm = XMFLOAT3(sinf(theta), 0.7f, cosf(theta));
            coneVertex[slice * 4 + 2].norm = XMFLOAT3(0.0f, -1.0f, 0.0f);
            coneVertex[slice * 4 + 3].norm = XMFLOAT3(0.0f, -1.0f, 0.0f);
        }
        // 最後の座標は先頭と同じ(誤差を修正)
        coneVertex[(kaku-1) * 4 + 0].pos = coneVertex[0].pos;
        coneVertex[(kaku-1) * 4 + 2].pos = coneVertex[2].pos;
    
        D3D11_SUBRESOURCE_DATA vertexBufferData = { 0 };
        vertexBufferData.pSysMem = coneVertex;
        vertexBufferData.SysMemPitch = 0;
        vertexBufferData.SysMemSlicePitch = 0;
        CD3D11_BUFFER_DESC vertexBufferDesc(sizeof(coneVertex), D3D11_BIND_VERTEX_BUFFER);
        DX::ThrowIfFailed(
            m_deviceResources->GetD3DDevice()->CreateBuffer(
                &vertexBufferDesc,
                &vertexBufferData,
                vertexBuffer
                ));
    
        static uint16 coneIndex[KAKU * 6];
        for (uint32 slice = 0; slice<(kaku - 1); slice++)
        {   // 辺の座標, 頂点座標, 蓋の座標, 蓋の中心
            coneIndex[6 * slice + 0] = slice * 4 + 0;
            coneIndex[6 * slice + 1] = slice * 4 + 1;
            coneIndex[6 * slice + 2] = slice * 4 + 4;
            coneIndex[6 * slice + 3] = slice * 4 + 6;
            coneIndex[6 * slice + 4] = slice * 4 + 3;
            coneIndex[6 * slice + 5] = slice * 4 + 2;
        }
        *indexCount = (kaku - 1) * 12;
    
        D3D11_SUBRESOURCE_DATA indexBufferData = { 0 };
        indexBufferData.pSysMem = coneIndex;
        indexBufferData.SysMemPitch = 0;
        indexBufferData.SysMemSlicePitch = 0;
        CD3D11_BUFFER_DESC indexBufferDesc(*indexCount, D3D11_BIND_INDEX_BUFFER);
        DX::ThrowIfFailed(
            m_deviceResources->GetD3DDevice()->CreateBuffer(
                &indexBufferDesc,
                &indexBufferData,
                indexBuffer));
    }
    

超初心者のプログラム入門(DirectX Store)