XLoader Color

XLoader Texture Color

Windows10 の XLoader で「頂点+法線+テクスチャ+色」のモデルを描画します。

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

XLoader Color Model

  1. Model Class と XLoader Class を組み込んで「頂点+法線+テクスチャ+色」のモデルを描画します。
    X-FILE のロードと描画は XLoader Class に任せます。
    頂点データの形式(頂点+法線+テクスチャ+色)に合わせてシェーダ関係を準備して下さい。
    Win10 XLoader を参照してベースプロジェクトを作成して下さい。
      System⇒App.cpp⇒App1Main⇒Model⇒XLoader
    
    XLoader ではありませんが、色を設定したモデルの描画は Win10 ColorModel を参照して下さい。
    テクスチャを設定したモデルの描画は Win10 TextureModel を参照して下さい。
  2. BoxP4TexAll.x を X Model からダウンロードしてプロジェクトに追加します。
    Win10 TextureModel では「頂点+法線+テクスチャ」で描画したので色情報は無視しましたが、今回はテクスチャに加えて色を設定して描画します。
    頂点+法線+テクスチャは対になっていて、頂点データとして組み合わせて格納します。
    色情報は X-FILE の MeshMaterialList で定義され、ポリゴン毎に設定されます。
    法線が設定されていない時は、プログラムで計算します。
    頂点座標とテクスチャ座標は、対になっていなければなりません。(普通のモデルは対になっている)。
    モデルの描画が大きすぎる(または小さすぎる)ときは Model Class のカメラで調整して下さい。
    テクスチャをロードするのに BasicReaderWriter.h(.cpp) と TextureLoader.h(.cpp) を使います。
    Windows10 DirectX Library から取得してプロジェクトに追加して下さい。
  3. シェーダ関係のファイルから説明します。
    テクスチャは二次元座標なので float2 を使います。
    シェーダーは Windows10 Shader から「頂点+法線+テクスチャ+4色」に対応した物を使って下さい。
    #pragma once
    #include "BasicMath.h"
    
    namespace App1
    {
        ・・・
    
        // 頂点シェーダーへの頂点ごとのデータの送信に使用します。
        struct VertexPosition
        {
            float3 pos;     // position
            float3 norm;    // norm
            float2 tex;     // Texture
            float4 color;   // color
        };
    }
    
  4. XLoader.h のソースコードです。
    X-FILE を解析してモデルのデータを作成して描画するための関数や領域を定義します。
    色(マテリアル)はポリゴン(面)ごとに設定されていて結構解析が面倒です。
    頂点データとポリゴンの関係を解析するのに構造体を使用します。
    vector<string> VT; に X-FILE のソースを行で切り分けて格納します。
    m_pos, m_norm, m_tex に頂点座標,法線ベクトル,テクスチャを格納します。
    #pragma once
    
    #include "..\Common\DeviceResources.h"
    #include "..\Common\StepTimer.h"
    #include "ShaderStructures.h"
    #include "BasicReaderWriter.h"
    #include "TextureLoader.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;
        float4              faceColor;
        float               power;
        float3              specularColor;
        float3              emissiveColor;
    };
    
    namespace App1
    {
        class XLoader
        {
        public:
            XLoader(const std::shared_ptr<DX::DeviceResources>& deviceResources);
            void Load();
            void Draw(ModelViewProjectionConstantBuffer);
            void Release();
    
        private:
            std::shared_ptr<DX::DeviceResources> m_deviceResources;
    
            Microsoft::WRL::ComPtr<ID3D11InputLayout>   m_inputLayout;
            Microsoft::WRL::ComPtr<ID3D11Buffer>        m_vertexBuffer;
            Microsoft::WRL::ComPtr<ID3D11Buffer>        m_indexBuffer;
            Microsoft::WRL::ComPtr<ID3D11VertexShader>  m_vertexShader;
            Microsoft::WRL::ComPtr<ID3D11PixelShader>   m_pixelShader;
            Microsoft::WRL::ComPtr<ID3D11Buffer>        m_constantBuffer;
            Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> m_textureSRV;
            Microsoft::WRL::ComPtr<ID3D11SamplerState>  m_sampler;
            ID3D11RasterizerState  *m_RasterizerState;
    
            uint32  m_indexCount;
            bool    m_loadingComplete;
    
            //★ 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<float3> m_pos;           // 頂点座標
            vector<float3> m_norm;          // 法線ベクトル
            vector<float2> m_tex;           // テクスチャ
            String^ m_texName;
            int     m_Line, m_Col, m_Top;
            string  Word;
            bool    m_normflag;             // 法線の取得フラグ
    
            void X_Byte(Array<byte>^ buf);
            bool Search(char *key);
            void Token();
            bool LineToken();
            bool Setfloat3(char *key, vector<float3> *f3);
            bool Setfloat2(char *key, vector<float2> *f2);
            void ComputeNorm(VertexPosition *Vertices, int siz);
            bool SetFTBL();
            bool SetNorm();
            void Convt_3P(bool flg);
            void SetMTBL();
            void MatList();
    
            void Debug(String^ msg);
            void Debug(String^ msg, int n);
            void Debug(char *str);
        };
    }
    
  5. XLoader.cpp のソースコードです。
    X-FILE を解析してモデルを作成して描画します。
    Load() 関数で X Model を取得して X_Byte(buf) 関数で解析します。
    Draw() 関数でモデルを描画します。
    Debug() 関数を組み込んでいますが、プログラムが完成すると不要です。
    // Texture モデル(pos + norm + 1tex + 4color)を描画
    #include "pch.h"
    #include "..\Common\DirectXHelper.h"
    #include "XLoader.h"
    
    using namespace App1;
    using namespace DirectX;
    using namespace Windows::Foundation;
    
    void XLoader::Debug(String^ msg)
    {   String^ message= ref new String(msg->Data());
        message+="\r\n";
        OutputDebugString(message->Data());
    }
    void XLoader::Debug(String^ msg, int n)
    {   String^ message= ref new String(msg->Data());
        message+= "  ";
        message+= n.ToString();
        message+="\r\n";
        OutputDebugString(message->Data());
    }
    void XLoader::Debug(char *str)
    {
        WCHAR   wk[1000];
        MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,str,-1,wk,1000);
        OutputDebugString(wk);
        OutputDebugString(L"\r\n");
    }
    
    XLoader::XLoader(const std::shared_ptr<DX::DeviceResources>& deviceResources) :
        m_loadingComplete(false),
        m_indexCount(0),
        m_deviceResources(deviceResources)
    {
    }
    
    // Load X-FILE
    void XLoader::Load()
    {
        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 },
                { "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 },
                { "COLOR",    0, DXGI_FORMAT_R32G32B32A32_FLOAT,0,32,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]() {
    
            // X-FILE を入力する
            BasicReaderWriter^ reader = ref new BasicReaderWriter();
            Array<byte>^ buf = reader->ReadData("BoxP4TexAll.x");
            X_Byte(buf);
    
            //★ pos, norm, tex を組み合わせて、モデルの頂点座標を作成
            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++)
            {   // 頂点座標の設定
                Vertices[i*3].pos = m_pos[POL3[i].Pos[0]];
                Vertices[i*3+1].pos = m_pos[POL3[i].Pos[1]];
                Vertices[i*3+2].pos = m_pos[POL3[i].Pos[2]];
                // 法線の設定(未定義の時は、プログラムで計算)
                if (m_normflag)
                {
                    Vertices[i*3].norm = m_norm[POL3[i].Norm[0]];
                    Vertices[i*3+1].norm = m_norm[POL3[i].Norm[1]];
                    Vertices[i*3+2].norm = m_norm[POL3[i].Norm[2]];
                }
                // テクスチャ座標の設定            
                Vertices[i*3].tex = m_tex[POL3[i].Pos[0]];
                Vertices[i*3+1].tex = m_tex[POL3[i].Pos[1]];
                Vertices[i*3+2].tex = m_tex[POL3[i].Pos[2]];
                // 色の設定(未定義のときは白色)
                int idx = POL3[i].Midx;
                if (idx != -1)
                {
                    Vertices[i*3].color = MTBL[idx].faceColor;
                    Vertices[i*3+1].color = MTBL[idx].faceColor;
                    Vertices[i*3+2].color = MTBL[idx].faceColor;
                }
                else
                {
                    Vertices[i*3].color.x = 1.0f;
                    Vertices[i*3].color.y = 1.0f;
                    Vertices[i*3].color.z = 1.0f;
                    Vertices[i*3].color.w = 1.0f;
                    Vertices[i*3+1].color = Vertices[i*3].color;
                    Vertices[i*3+2].color = Vertices[i*3].color;
                }
                Indices[i*3] = i*3;
                Indices[i*3+1] = i*3+1;
                Indices[i*3+2] = i*3+2;
            }
    
            // 法線ベクトルの計算
            if (m_normflag == false)
                ComputeNorm(Vertices, m_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, &m_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, &m_indexBuffer));
    
            // 領域の解放
            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));
    
            // カリング設定
            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));
        });
    
        // モデルが読み込まれたら、オブジェクトを描画する準備が完了します。
        createCubeTask.then([this] () {
            m_loadingComplete = true;
        });
    }
    
    // モデルを描画
    void XLoader::Draw(ModelViewProjectionConstantBuffer BuffData)
    {
        // 読み込みは非同期です。読み込みが完了した後にのみ描画してください。
        if (!m_loadingComplete)
        {   return; }
    
        auto context = m_deviceResources->GetD3DDeviceContext();
    
        // レンダー ターゲットを画面に設定します。
        ID3D11RenderTargetView *const targets[1] = { m_deviceResources->GetBackBufferRenderTargetView() };
        context->OMSetRenderTargets(1, targets, m_deviceResources->GetDepthStencilView());
    
        // 定数バッファーを準備して、グラフィックス デバイスに送信します。
        context->UpdateSubresource(
            m_constantBuffer.Get(),
            0, NULL, &BuffData, 0, 0);
    
        // 各頂点は、VertexPosition 構造体の 1 つのインスタンスです。
        UINT stride = sizeof(VertexPosition);
        UINT offset = 0;
        context->IASetVertexBuffers(
            0, 1, m_vertexBuffer.GetAddressOf(),
            &stride, &offset);
    
        context->IASetIndexBuffer(
            m_indexBuffer.Get(),
            DXGI_FORMAT_R16_UINT, // 各インデックスは、1 つの 16 ビット符号なし整数 (short) です。
            0);
    
        context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        context->IASetInputLayout(m_inputLayout.Get());
        context->RSSetState(m_RasterizerState);
    
        // 頂点シェーダーをアタッチします。
        context->VSSetShader(
            m_vertexShader.Get(), nullptr, 0);
    
        // 定数バッファーをグラフィックス デバイスに送信します。
        context->VSSetConstantBuffers(
            0, 1, m_constantBuffer.GetAddressOf());
    
        // ピクセル シェーダーをアタッチします。
        context->PSSetShader(m_pixelShader.Get(), nullptr, 0);
    
        context->PSSetShaderResources(0, 1, m_textureSRV.GetAddressOf());
        context->PSSetSamplers(0, 1, m_sampler.GetAddressOf());
    
        // オブジェクトを描画します。
        context->DrawIndexed(m_indexCount, 0, 0);
    }
    
    // 領域の解放
    void XLoader::Release()
    {
        m_loadingComplete = false;
        m_vertexShader.Reset();
        m_inputLayout.Reset();
        m_pixelShader.Reset();
        m_constantBuffer.Reset();
        m_vertexBuffer.Reset();
        m_indexBuffer.Reset();
    }
    
    //★X-FILE(buf) を入力して解析モデルデータを作成 
    void XLoader::X_Byte(Array<byte>^ buf)
    {
        int num, pt, pw;
        char *w;
    
        w = (char *)&(buf[0]);
        m_x = w;
    
        // String を行で切り分ける(m_x⇒VT)
        VT.clear();
        num = m_x.size();
        for (pt = 0; pt<num;)
        {
            pw = pt;
            for (pt++; pt<num && m_x[pt] != '\n'; pt++);
            if (pt >= num)  break;
            pt++;
            Word = Word.assign(m_x, pw, pt - pw);
            VT.push_back(Word);
        }
        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_pos.clear();
        m_Line = m_Top;
        if (Setfloat3("Mesh ", &m_pos))
        {
            SetFTBL();
        }
        else
        {   OutputDebugString(L"Mesh File  Format Error\r\n");
            return;
        }
    
        // 法線を取得
        m_norm.clear();
        m_Line = m_Top;
        m_normflag = false;
        if (Setfloat3("MeshNormals ", &m_norm))
        {
            m_normflag = SetNorm();
        }
    
        // Texture 座標を取得
        m_Line = m_Top;
        if (Setfloat2("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);
    
        // MeshMaterialList を取得
        MatList();
    
        // Face 構造体の FTBL ⇒ POL3
        Convt_3P(m_normflag);
    }
    
    //★ X-FILE 解析関数
    // Line から key で始まる行をサーチ ⇒ Line, Col に設定
    bool XLoader::Search(char *key)
    {
        for (; m_Line<VT_size - 1; m_Line++)
        {
            m_Col = VT[m_Line].find(key);
            if (m_Col != -1)    return true;
        }
        OutputDebugString(L"Keyword Not found\r\n");
        Debug(key);
        return false;
    }
    
    // Line, Col から次の Token を取得 ⇒ Word に設定
    void XLoader::Token()
    {
        int wk;
        Word = "";
        for (; m_Line<VT_size-1; m_Line++)
        {
            wk = VT[m_Line].find_first_not_of(" ,;{}\r\n", m_Col);
            m_Col = wk;
            wk = VT[m_Line].find_first_of(" ,;}\r\n", m_Col);
            if (wk>m_Col)
            {
                Word = Word.assign(VT[m_Line], m_Col, wk-m_Col);
                m_Col = wk + 1;
                return;
            }
            m_Col = 0;
        }
    }
    
    // Line, Col から次の Token を取得 ⇒ Word に設定(Line 行だけ検索)
    bool XLoader::LineToken()
    {
        int wk;
        Word= "";
        if (m_Col>=(int)VT[m_Line].size())  return false;
        wk= VT[m_Line].find_first_not_of(" ,;{}\r\n", m_Col);
        m_Col= wk;
        wk= VT[m_Line].find_first_of(" ,;}\r\n", m_Col);
        if (wk>m_Col)
        {   Word= Word.assign(VT[m_Line], m_Col, wk-m_Col);
            m_Col= wk + 1;
            return true;
        }
        return false;
    }
    
    // key を検索して f3 に三次元座標を設定
    bool XLoader::Setfloat3(char *key, vector<float3> *f3)
    {
        int     num, i;
        float   x, y, z;
        float3  wf3;
    
        if (Search(key) == false)
        {   return false;  }
    
        m_Line++;
        m_Col = 0;
        Token();
        num = atoi((char *)Word.data());
        if (num<3)
        {   return false;  }
    
        // 頂点座標を f3 に設定
        for (i = 0; i<num; i++)
        {
            Token();
            x = (float)atof((char *)Word.data());
            Token();
            y = (float)atof((char *)Word.data());
            Token();
            z = (float)atof((char *)Word.data());
            wf3.x = x;
            wf3.y = y;
            wf3.z = z;
            f3->push_back(wf3);
        }
        return true;
    }
    
    // key を検索して f2 に二次元座標を設定
    bool XLoader::Setfloat2(char *key, vector<float2> *f2)
    {
        int     num, i;
        float   x, y;
        float2  wf2;
    
        if (Search(key) == false)
        {   return false;  }
    
        m_Line++;
        m_Col = 0;
        Token();
        num = atoi((char *)Word.data());
        if (num<3)
        {   return false;  }
    
        // 頂点座標を f2 に設定
        for (i = 0; i<num; i++)
        {
            Token();
            x = (float)atof((char *)Word.data());
            Token();
            y = (float)atof((char *)Word.data());
            wf2.x = x;
            wf2.y = y;
            f2->push_back(wf2);
        }
        return true;
    }
    
    // 法線ベクトルの計算
    void XLoader::ComputeNorm(VertexPosition *Vertices, int siz)
    {
        for (int i = 0; i<siz; i += 3)
        {
            float ax = Vertices[i + 2].pos.x - Vertices[i + 1].pos.x;
            float ay = Vertices[i + 2].pos.y - Vertices[i + 1].pos.y;
            float az = Vertices[i + 2].pos.z - Vertices[i + 1].pos.z;
            float bx = Vertices[i + 0].pos.x - Vertices[i + 1].pos.x;
            float by = Vertices[i + 0].pos.y - Vertices[i + 1].pos.y;
            float bz = Vertices[i + 0].pos.z - Vertices[i + 1].pos.z;
    
            float nx = ay * bz - az * by;
            float ny = az * bx - ax * bz;
            float nz = ax * by - ay * bx;
    
            Vertices[i + 0].norm.x = nx;
            Vertices[i + 0].norm.y = ny;
            Vertices[i + 0].norm.z = nz;
            Vertices[i + 1].norm = Vertices[i + 0].norm;
            Vertices[i + 2].norm = Vertices[i + 0].norm;
        }
    }
    
    //★ "Material" で始まる行を検索(先頭から順に並ぶ)
    void XLoader::SetMTBL()
    {   Mat     mat;
    
        MTBL.clear();
        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 XLoader::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<midx.size(); i++)  Debug("MatIdx=",midx[i]);
        // fm, midx を参照して Face ごとの Midx を設定
        for(i=0; i<fcnt; i++)
        {   FTBL[i].Midx= midx[fm[i]];
        }
    }
    
    //★ 頂点に続く Index を vector<Face> に格納する
    bool XLoader::SetFTBL()
    {
        Face    face;
        int     i,j,num;
        unsigned short wk;
    
        FTBL.clear();
        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 XLoader::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 XLoader::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;
            }
        }
    }
    

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