OBJ Viewer

Win10 OBJ Viewer

Windows10 でハードディスク上の OBJ Model を選択して描画します。

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

概説

  1. File Picker を使うと直接ハードディスクからファイルを選択して入力することが出来ます。
    頂点データの数が uint16 を超えるビッグサイズの OBJ モデルも「頂点座標+法線+16色」で描画します。
    本来マテリアル(色やテクスチャ)は *.mtl に記述するのですが *.mtl を使わずに16色カラーをプリセットして描画します。
    Preset Color Model のプロジェクトは Win10 OBJ Preset Color Model を参照して下さい。
    ビッグサイズのモデルの描画は Win10 OBJ Big Normal Model を参照して下さい。
  2. File Picker を使うプロジェクトは DirectX 11 および XAML アプリ(ユニバーサル Windows)から生成します。
    デザイン画面(DirectXPage.xaml)に Button を貼り付けて、Button_Click() のハンドラをコーディングします。
    ボタンのクリックで OBJ モデルファイルを選択して読み込みます。
    OBJ モデルは普通 Shift_JIS でタイプされているので Binary Data としてそのまま読み込みます。
    入力した Binary Data を Model_Text(fileData) 関数で確認してみました。
    void OBJ_View::DirectXPage::Button_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
    {
        auto open = ref new FileOpenPicker();
        open->SuggestedStartLocation = PickerLocationId::Desktop;
        open->FileTypeFilter->Clear();
        open->FileTypeFilter->Append(".obj");
        create_task(open->PickSingleFileAsync()).then([this](StorageFile^ file)
        {   return FileIO::ReadBufferAsync(file);
        }).then([=](IBuffer^ buffer)
        {   auto fileData = ref new Platform::Array<byte>(buffer->Length);
        DataReader::FromBuffer(buffer)->ReadBytes(fileData);
        Model_Text(fileData);
        });
    }
    
  3. Shift_JIS のコードを WCHAR に変換して OutputDebugString() で印字する関数です。
    void DirectXPage::Model_Text(Array<byte>^ buf)
    {
        char    *wp;
        WCHAR   wk[10000];
        wp = (char *)&buf[0];
        MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, wp, -1, wk, 10000);
        OutputDebugString(wk);
    }
    

Text Data をリレーする

  1. プロジェクト名を OBJ_View に設定して DirectX 11 および XAML アプリ(ユニバーサル Windows)を生成します。
    DirectXPage.xaml.cpp の Button_Click で取得した Text Data を Load_OBJ Class までリレーします。
    プロジェクト名が OBJ_View なので OBJ_ViewMain(規定値では App1Main) になっています。
    DirectXPage.xaml ⇒ OBJ_ViewMain ⇒ Sample3DSceneRenderer ⇒ Load_OBJ
    
  2. DirectXPage.xaml にボタンを貼り付けて、Button_Click() のイベントハンドラを追加します。
    Button_Click 関数から File Picker で OBJ Model を選択します。
    m_main->Model_Text(fileData) を呼び出します。
  3. OBJ_ViewMain.h に Model_Text() を追加します。
    void Model_Text(Platform::Array<byte>^ text)
    {  m_sceneRenderer->Model_Text(text); }
    
  4. Sample3DSceneRenderer に Model_Text() を追加します。
    model->Text(text); で Text Data を設定して、model->Load() でモデルを生成します。
    void Sample3DSceneRenderer::Model_Text(Platform::Array<byte>^ text)
    {
        m_loadingComplete = false;
        model->Text(text);
        model->Load(&m_vertexBuffer, &m_indexBuffer, &m_indexCount);
        m_loadingComplete = true;
    }
    

モデルを描画

  1. model->Text(text); で OBJ モデルの Text を受け取る Text() 関数です。
    改行コードで切り分けて vector<string> VT; に格納します。
    void Load_OBJ::Text(Platform::Array<byte>^ text)
    {
        int     num, pt, pw;
    
        str = (char *)&(text[0]);
        VT.clear();
        num = str.size();
        for (pt = 0; pt < num;)
        {
            pw = pt;
            for (pt++; pt < num && str[pt] != '\n'; pt++);
            if (pt >= num)  break;
            pt++;
            Word = Word.assign(str, pw, pt - pw);
            VT.push_back(Word);
            for (; str[pt] == '\r' || str[pt] == '\n' || str[pt] == ' '; pt++);
        }
        VT_size = VT.size();
    }
    
  2. Win10 OBJ Normal Model などでは Load_OBJ::Load() 関数からファイル名を指定してモデルを入力しています。
    この処理は Load_OBJ::Text() 関数で行っているので OBJ モデルを入力する部分を削除して下さい。
    /* 削除して下さい
        BasicReaderWriter^ reader = ref new BasicReaderWriter();
        Array<byte>^ buf = reader->ReadData("cube.txt");
    
        // String を行で切り分ける(buf⇒VT)
        str = (char *)&(buf[0]);
        VT.clear();
        num = str.size();
        for (pt = 0; pt < num;)
        {   pw = pt;
            for (pt++; pt < num && str[pt] != '\n'; pt++);
            if (pt >= num)  break;
            pt++;
            Word = Word.assign(str, pw, pt - pw);
            VT.push_back(Word);
            for(; str[pt] == '\r' || str[pt] == '\n' || str[pt] == ' '; pt++);
        }
        VT_size = VT.size();
    */
    
  3. Win10 OBJ Preset Color Model を参照して、既定の色を設定して下さい。
    Load_OBJ::Load_OBJ(const std::shared_ptr<DX::DeviceResources>& deviceResources) :
        m_deviceResources(deviceResources)
    {
        MT_num = 0;
        Mat premat[16] = { { "white",  f3(1.0f, 1.0f, 1.0f) },
                           { "silver", f3(0.7f, 0.7f, 0.7f) },
                           { "grey",   f3(0.5f, 0.5f, 0.5f) },
                           { "black",  f3(0.0f, 0.0f, 0.0f) },
                           { "lime",   f3(0.0f, 1.0f, 0.0f) },
                           { "olive",  f3(0.5f, 0.5f, 0.0f) },
                           { "green",  f3(0.0f, 0.5f, 0.0f) },
                           { "teal",   f3(0.0f, 0.5f, 0.5f) },
                           { "auua",   f3(0.0f, 1.0f, 1.0f) },
                           { "red",    f3(1.0f, 0.0f, 0.0f) },
                           { "maroon", f3(0.5f, 0.0f, 0.0f) },
                           { "yellow", f3(1.0f, 1.0f, 0.0f) },
                           { "blue",   f3(0.0f, 0.0f, 1.0f) },
                           { "navy",   f3(0.0f, 0.0f, 0.5f) },
                           { "purple", f3(0.5f, 0.0f, 0.5f) },
                           { "fuchsia",f3(1.0f, 0.0f, 1.0f) } };
        MTBL.clear();
        for(uint32 k=0; k<16; k++)  MTBL.push_back(premat[k]);
    }
    
  4. Win10 OBJ Big Normal Model を参照して、ビッグサイズに対応して下さい。
    モデルのスケールは大小さまざまです。
    出来るだけ多くのモデルに対応出来るように次のコードを修正して下さい。
    【修正前】
        // このサンプルでは、行優先のマトリックスを使用した右辺座標系を使用しています。
        XMMATRIX perspectiveMatrix = XMMatrixPerspectiveFovRH(
            fovAngleY,
            aspectRatio,
            0.01f,
            100.0f
            );
    
    【修正後】
        // このサンプルでは、行優先のマトリックスを使用した右辺座標系を使用しています。
        XMMATRIX perspectiveMatrix = XMMatrixPerspectiveFovRH(
            fovAngleY,
            aspectRatio,
            0.01f,
            20000.0f
            );
    
  5. 一応これでプロジェクトは完成ですが、モデルによって描画サイズが大きすぎたり、小さすぎたりします。
    そこで私は、左右の矢印キーで描画サイズ(eye のZ座標)を調整出来るようにしています。
    またモデルの描画位置が中央より上になる場合もあり、上下の矢印キーで移動できるようにしました。
    さらに付け加えるなら、マウスの操作でモデルを回転出来るようにしたいですね。
    マウスやキーの操作は Win10 Model & Camera を参照して下さい。
        DirectX::XMVECTORF32 eye = { 0.0f, 0.1f, 4.0f, 0.0f };
        DirectX::XMVECTORF32 at = { 0.0f, 0.1f, 0.0f, 0.0f };
        DirectX::XMVECTORF32 up = { 0.0f, 1.0f, 0.0f, 0.0f };
        float   m_dy, m_dz;
    
    void Sample3DSceneRenderer::KeyDown(Windows::System::VirtualKey key)
    {   switch (key)
        {   case Windows::System::VirtualKey::Right:
                m_dz = 0.95f;
                break;
            case Windows::System::VirtualKey::Left:
                m_dz = 1.05f;
                break;
            case Windows::System::VirtualKey::Down:
                m_dy = 1.05f;
                break;
            case Windows::System::VirtualKey::Up:
                m_dy = 0.95f;
                break;
        }
    }
    

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