Load_X Texture

X-FILE を「頂点+法線+テクスチャ」でロードする Class を作成します。
テクスチャが設定されていないモデルは描画することが出来ません。

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

プログラムの説明

  1. Load_X Color は X-FILE を「頂点+色」モードでロードする Class です。
    Load_X Norm は X-FILE を「頂点+法線」モードでロードする Class です。
    Load_X Normal Color は X-FILE を「頂点+法線+色」モードでロードする Class です。
    次に「頂点+法線+テクスチャ」でロードする Class を作成します。
    テクスチャが設定されていないモデルは描画することが出来ないので注意して下さい。
    Texture モデル(X-FILE)の解析は Win10 TextureModel を参照して下さい。
  2. 自動生成されるプロジェクトの Sample3DSceneRenderer に Load_X Class を組み込んで描画します。
    DirectX 11 アプリ(ユニバーサル Windows)のプロジェクトを生成して下さい。
    生成したアプリは「頂点+カラー」のモデルを描画するプロジェクトなのでシェーダー関係は「頂点+法線+テクスチャ」の物に入れ替えます。
    Load_X.h(.cpp) のソースコードはこの後に掲載します。
    プロジェクトを生成は Win10 Template を参照して下さい。
  3. X-FILE("BoxP3TexAll.x") とテクスチャ画像(star.jpg)をプロジェクトに加えます。
    X-FILE を右クリックしてコンテンツを True に設定します。
  4. Sample3DSceneRenderer.h で Load_X.h を組み込んで、Load_X Class の領域を定義します。
    #include "Load_X.h"
    
        Load_X  *model;
    
  5. 頂点データの定義を次のように修正して下さい。
        static const D3D11_INPUT_ELEMENT_DESC vertexDesc [] =
        {
            { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
            { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
            { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 },
        };
    
  6. CreateDeviceDependentResources() 関数で X-FILE をロードします。
    「両方のシェーダーの読み込みが完了したら、メッシュを作成します。」以降を書き換えて下さい。
    プロジェクトに加えられた "BoxP3TexAll.x" をロードして &m_vertexBuffer, &m_indexBuffer, &m_indexCount に設定します。
    void Sample3DSceneRenderer::CreateDeviceDependentResources()
    {
        ・・・
    
        // 両方のシェーダーの読み込みが完了したら、メッシュを作成します。
        auto createCubeTask = (createPSTask && createVSTask).then([this] () {
            model = new Load_X(m_deviceResources);
            model->Load("BoxP3TexAll.x", &m_vertexBuffer, &m_indexBuffer, &m_indexCount);
        });
    
        // メッシュが読み込まれたら、オブジェクトを描画する準備が完了します。
        createCubeTask.then([this] () {
            m_loadingComplete = true;
        });
    }
    
  7. Rotate() 関数でモデルを回転しているのですが、修正する必要はありません。
    X-FILE ではポリゴンが裏返るので Render() 関数でカリングモードを設定して描画します。
    モデルが大きすぎる(小さすぎる)ときは eye のZ座標で調整して下さい。
    void Sample3DSceneRenderer::Render()
    {
        // 読み込みは非同期です。読み込みが完了した後にのみ描画してください。
        if (!m_loadingComplete)
        {   return;  }
    
        ・・・
    
        // テクスチャを設定
        model->SetTex();
    
        // カリングを設定
        model->SetCullMode();
    
        // オブジェクトを描画します。
        context->DrawIndexed(m_indexCount, 0, 0);
    
  8. Load_X.h のソースコードです。
    X-FILE を解析してモデルのデータを作成するための関数や領域を定義します。
    CreateModel Class に習って CreateModel() 関数を使ってみました。
    #pragma once
    
    #include <vector>
    #include "..\Common\DeviceResources.h"
    #include "..\Common\StepTimer.h"
    #include "ShaderStructures.h"
    #include "BasicReaderWriter.h"
    #include "BasicMath.h"
    #include "TextureLoader.h"
    
    using namespace std;
    using namespace Platform;
    
    namespace App1
    {
        class Load_X
        {
        public:
            Load_X(const std::shared_ptr<DX::DeviceResources>& deviceResources);
            void Load(
                _In_  Platform::String ^str,
                _Out_ ID3D11Buffer **vertexBuffer,
                _Out_ ID3D11Buffer **indexBuffer,
                _Out_ uint32 *indexCount);
            void CreateModel(
                _Out_ ID3D11Buffer **vertexBuffer,
                _Out_ ID3D11Buffer **indexBuffer,
                _In_  uint32 *indexCount,
                _In_  VertexPosition *Vertices,
                _In_  uint16 *Indices,
                _In_  uint32 Size);
            void SetCullMode();
            void SetTex();
            Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> m_textureSRV;
            Microsoft::WRL::ComPtr<ID3D11SamplerState>  m_sampler;
    
        private:
            std::shared_ptr<DX::DeviceResources> m_deviceResources;
            ID3D11RasterizerState  *m_RasterizerState;
    
            //★ X-FILE の領域
            std::string     m_x;                // X-FILE TEXT
            vector<string>  VT;                 // X-FILE を行で切り分け
            int             VT_size;            // VT の大きさ
            vector<DirectX::XMFLOAT3> m_pos;    // 頂点座標
            vector<DirectX::XMFLOAT3> m_norm;   // 法線ベクトル
            vector<DirectX::XMFLOAT2> m_tex;    // テクスチャ座標
            vector<unsigned short> m_idxP;      // 頂点 Index の並び(角付)
            vector<unsigned short> m_idxN;      // 法線 Index の並び(角付)
            vector<unsigned short> m_idxP3;     // 頂点 Index(3P) の並び
            vector<unsigned short> m_idxN3;     // 法線 Index(3P) の並び
            String^ m_texName;
            int     m_Line, m_Col, m_Top;
            string  Word;
    
            bool SetXMFLOAT3(char *key, vector<DirectX::XMFLOAT3> *f3);
            bool SetXMFLOAT2(char *key, vector<DirectX::XMFLOAT2> *f2);
            bool SetShort(vector<unsigned short> *val);
            void PX_P3(vector<unsigned short> PX, vector<unsigned short> *P3);
            void ComputeNorm(VertexPosition *Vertices, int siz);
            bool Search(char *);
            void Token();
        };
    }
    
  9. Load_X.cpp のソースコードです。
    Load() 関数でモデル(X-FILE)を解析して vertexBuffer, indexBuffer, indexCount に設定します。
    掲載されていない関数は Windows10 DirectX Library を参照して下さい。
    SetCullMode() はポリゴンが裏返るとき、カリングを設定する関数です。
    SetTex() はテクスチャを設定する関数です。
    // Texture モデル(pos + norm + tex)をロード
    #include "pch.h"
    #include "..\Common\DirectXHelper.h"
    #include "Load_X.h"
    
    using namespace App1;
    using namespace DirectX;
    using namespace Windows::Foundation;
    
    Load_X::Load_X(const std::shared_ptr<DX::DeviceResources>& deviceResources) :
        m_deviceResources(deviceResources)
    {
    }
    
    // モデルを生成
    void Load_X::CreateModel(
        _Out_ ID3D11Buffer **vertexBuffer,
        _Out_ ID3D11Buffer **indexBuffer,
        _In_  uint32 *indexCount,
        _In_  VertexPosition *Vertices,
        _In_  uint16 *Indices,
        _In_  uint32 Size)
    {
        D3D11_SUBRESOURCE_DATA vertexBufferData = {0};
        vertexBufferData.pSysMem = Vertices;
        vertexBufferData.SysMemPitch = 0;
        vertexBufferData.SysMemSlicePitch = 0;
        CD3D11_BUFFER_DESC vertexBufferDesc(Size, D3D11_BIND_VERTEX_BUFFER);
        DX::ThrowIfFailed(
            m_deviceResources->GetD3DDevice()->CreateBuffer(
                &vertexBufferDesc,
                &vertexBufferData,
                vertexBuffer
            ));
    
        D3D11_SUBRESOURCE_DATA indexBufferData = {0};
        indexBufferData.pSysMem = Indices;
        indexBufferData.SysMemPitch = 0;
        indexBufferData.SysMemSlicePitch = 0;
        CD3D11_BUFFER_DESC indexBufferDesc(*indexCount * 2, D3D11_BIND_INDEX_BUFFER);
        DX::ThrowIfFailed(
            m_deviceResources->GetD3DDevice()->CreateBuffer(
                &indexBufferDesc,
                &indexBufferData,
                indexBuffer));
    
        // カリング設定
        D3D11_RASTERIZER_DESC rasterizerDesc;
        ZeroMemory( &rasterizerDesc, sizeof( D3D11_RASTERIZER_DESC ) );
        //rasterizerDesc.CullMode = D3D11_CULL_BACK;
        rasterizerDesc.CullMode = D3D11_CULL_FRONT;
        rasterizerDesc.FillMode = D3D11_FILL_SOLID;
        rasterizerDesc.DepthClipEnable = FALSE;
        rasterizerDesc.MultisampleEnable = TRUE;
        rasterizerDesc.DepthBiasClamp = 0;
        rasterizerDesc.SlopeScaledDepthBias = 0;
        DX::ThrowIfFailed(
            m_deviceResources->GetD3DDevice()->CreateRasterizerState(
                &rasterizerDesc, &m_RasterizerState));
    }
    
    // Load X-FILE
    void Load_X::Load(
        _In_ Platform::String ^str,
        _Out_ ID3D11Buffer **vertexBuffer,
        _Out_ ID3D11Buffer **indexBuffer,
        _Out_ uint32 *indexCount)
    {
        //★ X-FILE を入力して解析
        int     num;
        int     pt,pw;
        byte    wk;
    
        VT.clear();
        m_pos.clear();
        m_norm.clear();
        m_tex.clear();
        m_idxP.clear();
        m_idxN.clear();
        m_idxP3.clear();
        m_idxN3.clear();
    
        m_sampler = nullptr;
        m_texName = "";
        BasicReaderWriter^ reader = ref new BasicReaderWriter();
        Platform::Array<byte>^ buf = reader->ReadData(str);
    
        // X-FILE を行で切り分けて vector<string> VT に格納
        num= buf->Length;
        for(pt=0; pt<num; )
        {   pw= pt;
            for(pt++; pt<num && buf->Data[pt]!=0x0A; pt++);
            if (pt>=num)    break;
            pt++;
            wk= buf->Data[pt];
            buf->Data[pt]= 0x00;
            VT.push_back((char *)buf->Data+pw);
            buf->Data[pt]= wk;
        }
        VT.push_back("}\r\n");
        VT_size= VT.size();
    
        // Header Check("xof 0303txt 0032")
        m_Col= VT[0].find("xof");
        if (m_Col==-1)
        {   OutputDebugString(L"Invalid  Mesh File");
            return;
        }
        m_Col= VT[0].find("txt");
        if (m_Col==-1)
        {   OutputDebugString(L"Mesh File  Format Error");
            return;
        }
    
        // template をスキップ
        m_Line= 1;
        m_Col= 0;
        while(true)
        {   Token();
            if (_strnicmp((char *)Word.data(),"template",8)!=0) break;
            Search("}");
            m_Line++;
        }
        m_Top= m_Line;
    
        // 頂点座標を取得
        m_Line = m_Top;
        if (SetXMFLOAT3("Mesh ", &m_pos))
        {
            SetShort(&m_idxP);
        }
        else
        {
            OutputDebugString(L"Mesh File  Format Error");
            return;
        }
    
        // 法線を取得(無いときは自動計算)
        m_Line = m_Top;
        if (SetXMFLOAT3("MeshNormals ", &m_norm))
        {   SetShort(&m_idxN);  }
    
        // Texture 座標を取得
        m_Line = m_Top;
        if (SetXMFLOAT2("MeshTextureCoords ", &m_tex))
        {
            if (m_pos.size() != m_tex.size())
            {
                OutputDebugString(L"頂点座標の数 ≠ テクスチャ座標の数");
                return;
            }
        }
        else
        {
            OutputDebugString(L"MeshTextureCoords Error");
            return;
        }
    
        // Texture Name を取得
        m_Line = m_Top;
        if (Search("TextureFilename")==false)
        {
            OutputDebugString(L"TextureFilename Not found");
            return;
        }
        m_Line++;
        m_Col= 0;
        Token();
        Word = Word.substr(1, Word.size() - 2);
        WCHAR   work[256];
        MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, Word.data(), -1, work, 256);
        m_texName = ref new String(work);
    
        // N角ポリゴン ⇒ 3角ポリゴン
        PX_P3(m_idxP, &m_idxP3);
        PX_P3(m_idxN, &m_idxN3);
    
        // 頂点データとインデックスデータを作成
        unsigned vtx_size = m_idxP3.size();
        VertexPosition *Vertices = new VertexPosition[vtx_size];
        unsigned short *Indices = new unsigned short[vtx_size];
    
        for(unsigned i=0; i<vtx_size; i++)
        {
            Vertices[i].pos = m_pos[m_idxP3[i]];
            Vertices[i].tex = m_tex[m_idxP3[i]];
            if (m_idxN3.size()>i)   Vertices[i].norm = m_norm[m_idxN3[i]];
            Indices[i] = i;
        }
    
        // 法線ベクトルの計算
        if (m_idxN3.size()<vtx_size)
            ComputeNorm(Vertices, vtx_size);
    
        // モデルを生成
        *indexCount = m_idxP3.size();
        CreateModel(vertexBuffer, indexBuffer, indexCount, Vertices, Indices, sizeof(VertexPosition)*(*indexCount));
    
        // 領域の解放
        if (Vertices)   delete Vertices;
        if (Indices)    delete Indices;
    
        // テクスチャのロード
        TextureLoader^ loader =
            ref new TextureLoader(m_deviceResources->GetD3DDevice());
        loader->LoadTexture(m_texName, nullptr, &m_textureSRV);
    
        // create the sampler
        D3D11_SAMPLER_DESC samplerDesc;
        ZeroMemory(&samplerDesc, sizeof(D3D11_SAMPLER_DESC));
        samplerDesc.Filter = D3D11_FILTER_ANISOTROPIC;
        samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
        samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
        samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
        samplerDesc.MipLODBias = 0.0f;
        samplerDesc.MaxAnisotropy = 2;
        samplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
        samplerDesc.BorderColor[0] = 0.0f;
        samplerDesc.BorderColor[1] = 0.0f;
        samplerDesc.BorderColor[2] = 0.0f;
        samplerDesc.BorderColor[3] = 0.0f;
        samplerDesc.MinLOD = 0;
        samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;
    
        DX::ThrowIfFailed(
            m_deviceResources->GetD3DDevice()->CreateSamplerState(
            &samplerDesc, &m_sampler));
    }
    
    // ★XLoader 解析メソッド
    // カリングの設定
    void Load_X:: SetCullMode()
    {
        auto context = m_deviceResources->GetD3DDeviceContext();
        context->RSSetState(m_RasterizerState);
    }
    
    // テクスチャの設定
    void Load_X::SetTex()
    {
        auto context = m_deviceResources->GetD3DDeviceContext();
        context->PSSetSamplers(0, 1, m_sampler.GetAddressOf());
        context->PSSetShaderResources(0, 1, m_textureSRV.GetAddressOf());
    }
    

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