Load_X Normal Color

X-FILE を「頂点+法線+4色」でロードする Class を作成します。
カラーが設定されていれば、法線の無いモデルやテクスチャモデルも「頂点+法線+色」でロード出来ます。

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

プログラムの説明

  1. Load_X Color は X-FILE を「頂点+3色」モードでロードする Class です。
    Load_X Norm は X-FILE を「頂点+法線」モードでロードする Class です。
    Load_X Color, Load_X Norm に続いて「頂点+法線+4色」でロードする Class を作成します。
    色モデルは「頂点+法線+色」が一般的で、「頂点+色」のモデルも「頂点+法線+4色」でロードしてくれます。
    「頂点座標+法線+色」モデルの解析は Color Model で詳しく説明されています。
  2. 自動生成されるプロジェクトの Sample3DSceneRenderer に Load_X Class を組み込んで描画します。
    DirectX 11 アプリ(ユニバーサル Windows)のプロジェクトを生成して下さい。
    生成したアプリは「頂点+カラー」のモデルを描画するプロジェクトなのでシェーダー関係は「頂点+法線+4色」の物に入れ替えます。
    Load_X.h(.cpp) のソースコードはこの後に掲載します。
    プロジェクトを生成は Win10 Template を参照して下さい。
  3. X-FILE("gal2.x") をプロジェクトに加えます。
    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 },
            { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 }, 
        };
    
  6. CreateDeviceDependentResources() 関数で X-FILE をロードします。
    「両方のシェーダーの読み込みが完了したら、メッシュを作成します。」以降を書き換えて下さい。
    プロジェクトに加えられた "gal2.x" をロードして &m_vertexBuffer, &m_indexBuffer, &m_indexCount に設定します。
    void Sample3DSceneRenderer::CreateDeviceDependentResources()
    {
        ・・・
    
        // 両方のシェーダーの読み込みが完了したら、メッシュを作成します。
        auto createCubeTask = (createPSTask && createVSTask).then([this] () {
            model = new Load_X(m_deviceResources);
            //model->Load("ConeColor.x", &m_vertexBuffer, &m_indexBuffer, &m_indexCount);
            model->Load("gal2.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->SetCullMode();
    
        // オブジェクトを描画します。
        context->DrawIndexed(m_indexCount, 0, 0);
    
  8. Load_X.h のソースコードです。
    X-FILE を解析してモデルのデータを作成するための関数や領域を定義します。
    今回は float3 を使わずにコーディングしてみました。
    // Pos + Norm + Color モデル
    #pragma once
    
    #include "..\Common\DeviceResources.h"
    #include "..\Common\StepTimer.h"
    #include "ShaderStructures.h"
    #include "BasicReaderWriter.h"
    #include <vector>
    
    using namespace std;
    using namespace Platform;
    
    // Face 構造体(FTBL)
    struct  Face
    {   int         Num;        // 頂点数
        vector<unsigned short>  Pos;    // 頂点 Index の並び
        vector<unsigned short>  Norm;   // 法線 Index の並び
        int         Midx;       // Material(MTBL) Index(-1:指定なし)
    };
    // Polygon3 構造体(POL3)
    struct  Pol3
    {
        unsigned short  Pos[3];     // 頂点 Index の並び
        unsigned short  Norm[3];    // 法線 Index の並び
        int     Midx;               // Material(MTBL) Index
    };
    // Material 構造体(MTBL)
    struct  Mat
    {   string                  matName;
        DirectX::XMFLOAT3       faceColor;
        float                   power;
        DirectX::XMFLOAT3       specularColor;
        DirectX::XMFLOAT3       emissiveColor;
    };
    
    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 SetCullMode();
    
        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<Face>    FTBL;           // Face Table
            vector<Pol3>    POL3;           // Polygon3 Table
            vector<Mat>     MTBL;           // Material Table
    
            vector<DirectX::XMFLOAT3> m_pos;    // 頂点座標
            vector<DirectX::XMFLOAT3> m_norm;   // 法線ベクトル
            int     m_Line, m_Col, m_Top;
            uint32  m_indexCount;
            bool    m_normflag;             // 法線の取得フラグ
            string  Word;
    
            bool Search(char *key);
            void Token();
            bool LineToken();
            bool SetXMFLOAT3(char *key, vector<DirectX::XMFLOAT3> *f3);
            void ComputeNorm(VertexPosition *Vertices, int siz);
    
            bool SetFTBL();
            bool SetNorm();
            void Convt_3P(bool flg);
            void SetMTBL();
            void MatList();
    
            void Debug(String^ msg, int n);
            void X_Source(Array<byte>^ buf);
        };
    }
    
  9. Load_X.cpp のソースコードです。
    Load() 関数でモデル(X-FILE)を解析して vertexBuffer, indexBuffer, indexCount に設定します。
    掲載されていない関数は Windows10 DirectX Library を参照して下さい。
    SetCullMode() はポリゴンが裏返るとき、カリングを設定する関数です。
    // Pos + Norm + Color モデル
    #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)
    {
    }
    
    // Load X-FILE
    void Load_X::Load(
        _In_ Platform::String ^str,
        _Out_ ID3D11Buffer **vertexBuffer,
        _Out_ ID3D11Buffer **indexBuffer,
        _Out_ uint32 *indexCount)
    {
        int num, pt, pw;
        byte wk;
    
        VT.clear();
        FTBL.clear();
        POL3.clear();
        MTBL.clear();
        m_pos.clear();
        m_norm.clear();
    
        // ★X-FILE を入力して解析
        BasicReaderWriter^ reader = ref new BasicReaderWriter();
        Array<byte>^ buf = reader->ReadData(str);
    //X_Source(buf);
    
        // 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)    {  return;  }
        m_Col = VT[0].find("txt");
        if (m_Col == -1)    {  return;  }
    
        // template をスキップ
        m_Line = 1;
        m_Col = 0;
        while (true)
        {
            Token();
            if (Word != "template") break;
            Search("}");
            m_Line++;
        }
        m_Top = m_Line;
    
        // Material(MTBL)を取得
        SetMTBL();
    
        // 頂点座標を取得
        m_Line = m_Top;
        if (SetXMFLOAT3("Mesh ", &m_pos))
        {
            SetFTBL();
        }
        else
        {
            OutputDebugString(L"Mesh File  Format Error\r\n");
            return;
        }
    
        // 法線を取得
        m_Line = m_Top;
        m_normflag = false;
        if (SetXMFLOAT3("MeshNormals ", &m_norm))
        {
            m_normflag = SetNorm();
        }
    
        // MeshMaterialList を取得
        MatList();
    
        // Face 構造体の FTBL ⇒ POL3
        Convt_3P(m_normflag);
    
        //★ pos, norm, color を組み合わせて、モデルの頂点座標を作成
        m_indexCount = POL3.size() * 3;
    
        VertexPosition *Vertices = new VertexPosition[m_indexCount];
        unsigned short *Indices = new unsigned short[m_indexCount];
    
        for(unsigned i=0; i<POL3.size(); i++)
        {   for(unsigned j=0; j<3; j++)
            {   Vertices[i*3+j].pos = m_pos[POL3[i].Pos[j]];
                if (m_normflag) Vertices[i*3+j].norm = m_norm[POL3[i].Norm[j]];
                else        Vertices[i*3+j].norm= XMFLOAT3(0.0f, 0.0f, 0.0f);
    
                //Vertices[i*3+j].color= XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f);
                int idx= POL3[i].Midx;
                if (idx != -1)
                {
                    Vertices[i * 3 + j].color.x = MTBL[idx].faceColor.x;
                    Vertices[i * 3 + j].color.y = MTBL[idx].faceColor.y;
                    Vertices[i * 3 + j].color.z = MTBL[idx].faceColor.z;
                    Vertices[i * 3 + j].color.w = 1.0f;
                }
                else
                    Vertices[i*3+j].color= XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
    
                Indices[i*3+j]= i*3+j;
            }
        }
    
        // 法線ベクトルの計算
        if (m_normflag==false)
            ComputeNorm(Vertices, *indexCount);
    
        // メッシュを生成
        D3D11_SUBRESOURCE_DATA vertexBufferData = { 0 };
        vertexBufferData.pSysMem = Vertices;
        vertexBufferData.SysMemPitch = 0;
        vertexBufferData.SysMemSlicePitch = 0;
        CD3D11_BUFFER_DESC vertexBufferDesc(sizeof(VertexPosition)*m_indexCount, 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(sizeof(unsigned short)*m_indexCount, D3D11_BIND_INDEX_BUFFER);
        DX::ThrowIfFailed(
            m_deviceResources->GetD3DDevice()->CreateBuffer(
                &indexBufferDesc, &indexBufferData, indexBuffer));
    
        *indexCount = m_indexCount;
    
        // 領域の解放
        if (Vertices)   delete Vertices;
        if (Indices)    delete Indices;
    
        // カリング設定
        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));
    }
    
    //★ X-FILE 解析関数
    //★ "Material" で始まる行を検索(先頭から順に並ぶ)
    void Load_X::SetMTBL()
    {   Mat     mat;
    
        m_Line= m_Top;
        while(m_Line<VT_size)
        {   Search("Material ");
            if (m_Col==-1)  break;
            LineToken();
            LineToken();        // 二回目で名前を取得
            mat.matName= Word;
            // "Material" に続く色を格納する
            m_Line++;
            m_Col= 0;
            Token();
            mat.faceColor.x= (float)atof(Word.data());
            Token();
            mat.faceColor.y= (float)atof(Word.data());
            Token();
            mat.faceColor.z= (float)atof(Word.data());
            Token();
            MTBL.push_back(mat);
        }
    }
    
    //★ "MeshMaterialList" で始まる行を検索
    void Load_X::MatList()
    {
        vector<int> fm;     // Face Material
        vector<int> midx;   // Material(MTBL) の Index
        int         fcnt,num,i;
        unsigned    j;
    
        m_Line= m_Top;
        Search("MeshMaterialList ");
        if (m_Col==-1)  return;
    
        m_Line++;
        m_Col= 0;
        Token();
        num= atoi(Word.data());
        Token();
        fcnt= atoi(Word.data());
        if (FTBL.size()!=fcnt)
        {   Debug(L"Mesh Face= ", FTBL.size());
            Debug(L"MeshMaterialList Face= ", fcnt);
            return;
        }
    
        // Face に対応する Material番号を設定
        for(i=0; i<fcnt; i++)
        {   Token();
            fm.push_back(atoi(Word.data()));
        }
        // Material 名で検索して、Material Index を設定
        for(i=0; i<num; i++)
        {   Token();
            if (Word!="Material")
            {
                for(j=0; j<MTBL.size(); j++)
                {   if (MTBL[j].matName==Word)  break;
                }
                if (j<MTBL.size())  midx.push_back(j);
                else    midx.push_back(j);
            }
            else    midx.push_back(i);
            Search("}");
        }
    
        for(i=0; i<fcnt; i++)
        {   FTBL[i].Midx= midx[fm[i]];  }
    }
    
    //★ 頂点に続く Index を vector<Face> に格納する
    bool Load_X::SetFTBL()
    {
        Face    face;
        int     i,j,num;
        unsigned short wk;
    
        Token();
        num = atoi(Word.data());
        if (num<3)
        {   return false;   }
    
        face.Midx= -1;
        for(i=0; i<num; i++)
        {
            Token();
            face.Num= atoi(Word.data());
            face.Pos.clear();
            for(j=0; j<face.Num; j++)
            {
                Token();
                wk= atoi(Word.data());
                face.Pos.push_back(wk);
            }
            FTBL.push_back(face);
        }
        return true;
    }
    
    // 法線に続く Index を Face(FTBL) の Norm に格納する
    bool Load_X::SetNorm()
    {
        int     i,j,num,cn;
        unsigned short wk;
    
        Token();
        num= atoi(Word.data());
        if (FTBL.size()!=num)
        {   OutputDebugString(L"MeshNormals Face Count Error");
            return false;
        }
        for(i=0; i<num; i++)
        {   Token();
            cn= atoi(Word.data());
            for(j=0; j<cn; j++)
            {
                Token();
                wk= atoi(Word.data());
                FTBL[i].Norm.push_back(wk);
            }
        }
        return true;
    }
    
    // vector<Face> FTBL ⇒ vector<Pol3> POL3  TRIANGLELIST に変換
    // bool flg は法線ベクトルが定義されているか否かの区別
    void Load_X::Convt_3P(bool flg)
    {   Pol3   pol3;
        int    i, j;
    
        for(i=0; i<(int)FTBL.size(); i++)
        {
            pol3.Midx= FTBL[i].Midx;
            switch(FTBL[i].Num)
            {   case 3:     //TRIANGLELIST
                    pol3.Pos[0]= FTBL[i].Pos[0];
                    pol3.Pos[1]= FTBL[i].Pos[1];
                    pol3.Pos[2]= FTBL[i].Pos[2];
                    if (flg)
                    {
                        pol3.Norm[0]= FTBL[i].Norm[0];
                        pol3.Norm[1]= FTBL[i].Norm[1];
                        pol3.Norm[2]= FTBL[i].Norm[2];
                    }
                    POL3.push_back(pol3);
                    break;
                case 4:     //TRIANGLESTRIP 4
                    pol3.Pos[0]= FTBL[i].Pos[0];
                    pol3.Pos[1]= FTBL[i].Pos[1];
                    pol3.Pos[2]= FTBL[i].Pos[2];
                    if (flg)
                    {   pol3.Norm[0]= FTBL[i].Norm[0];
                        pol3.Norm[1]= FTBL[i].Norm[1];
                        pol3.Norm[2]= FTBL[i].Norm[2];
                    }
                    POL3.push_back(pol3);
                    pol3.Pos[0]= FTBL[i].Pos[0];
                    pol3.Pos[1]= FTBL[i].Pos[2];
                    pol3.Pos[2]= FTBL[i].Pos[3];
                    if (flg)
                    {   pol3.Norm[0]= FTBL[i].Norm[0];
                        pol3.Norm[1]= FTBL[i].Norm[2];
                        pol3.Norm[2]= FTBL[i].Norm[3];
                    }
                    POL3.push_back(pol3);
                    break;
                default:    //TRIANGLEFAN
                    for(j=0; j<=FTBL[i].Num-3; j++)
                    {
                        pol3.Pos[0]= FTBL[i].Pos[0];
                        pol3.Pos[1]= FTBL[i].Pos[j+1];
                        pol3.Pos[2]= FTBL[i].Pos[j+2];
                        if (flg)
                        {
                            pol3.Norm[0]= FTBL[i].Norm[0];
                            pol3.Norm[1]= FTBL[i].Norm[j+1];
                            pol3.Norm[2]= FTBL[i].Norm[j+2];
                        }
                        POL3.push_back(pol3);
                    }
                    break;
            }
        }
    }
    
    // カリングの設定
    void Load_X:: SetCullMode()
    {
        auto context = m_deviceResources->GetD3DDeviceContext();
        context->RSSetState(m_RasterizerState);
    }
    

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