111.マルチメッシュ

 このサンプルはFullTutorial011というディレクトリに含まれます。
 BaseCrossDx11.slnというソリューションを開くとDx11版が起動します。
 リビルドして実行すると以下の画面が出てきます。リリースモードでのビルトをお勧めします。

 

図0111a

 


マルチメッシュとは

 今項のテーマはマルチメッシュです。どういう機能かというと複数のメッシュをまとめる機能です。BaseCrossには複数のプリミティブ(球体や立方体やコーン、トーラスなど)が実装可能ですが、それぞれは単純な形状です。ですからモデルを使わずにこれらの形状だけでゲームを作製しようとすると、なんとなく単純すぎる場合があります。
 言い方を変えれば3Dモデル作成ツールを使わずにゲームを作成する場合、単純な形状のみになってしまいますので、それらを組み合わせて一つのオブジェクトにする機能というわけです。
 上記実行画面を見ていただくとわかりますが、画面上にはが配置されています。このオブジェクトは1つのシリンダーと2つのコーンを組み合わせて作成したマルチメッシュです。
 この項のサンプルはこのようなメッシュを組み合わせたメッシュの作成方法を紹介しています。
 なおこのサンプルの以外のオブジェクトは、FullTutorial004と同じです。

マルチメッシュとは

 のクラスはTreeクラスです。Character.h/cppに実装があります。以下がヘッダ部です。
class Tree : public GameObject {
    Vec3 m_Pos;
    shared_ptr<MultiMeshResource> m_MultiMeshResource;
public:
    //構築と破棄
    //--------------------------------------------------------------------------------------
    /*!
    @brief  コンストラクタ
    @param[in]  StagePtr    ステージ
    */
    //--------------------------------------------------------------------------------------
    Tree(const shared_ptr<Stage>& StagePtr, const Vec3& Pos);
    //--------------------------------------------------------------------------------------
    /*!
    @brief  デストラクタ
    */
    //--------------------------------------------------------------------------------------
    virtual ~Tree() {}
    //--------------------------------------------------------------------------------------
    /*!
    @brief  初期化
    */
    //--------------------------------------------------------------------------------------
    virtual void OnCreate() override;
    //--------------------------------------------------------------------------------------
    /*!
    @brief  更新
    */
    //--------------------------------------------------------------------------------------
    virtual void OnUpdate() override {}
};
 このように実装する関数はごくわずかです。変化もしませんのでOnUpdate()仮想関数は空関数となっています。
 赤くなっている部分がマルチメッシュです。マルチメッシュはインスタンスごとに保持していてもメモリは圧迫しませんので、メンバ変数としています。

 以下が実体部のTree::OnCreate()関数です。もっぱらマルチメッシュ関連の実装になります。
void Tree::OnCreate() {
    auto PtrTransform = GetComponent<Transform>();
    PtrTransform->SetPosition(m_Pos);
    PtrTransform->SetScale(0.5f, 1.0f, 0.5f);
    PtrTransform->SetRotation(0.0f, 0.0f, 0.0f);
    //capsuleの衝突判定をつける
    auto PtrColl = AddComponent<CollisionCapsule>();
    PtrColl->SetFixed(true);
//      PtrColl->SetDrawActive(true);

    shared_ptr<MeshResource> TreeMesh, LeafMesh1, LeafMesh2;
    if (!App::GetApp()->CheckResource<MeshResource>(L"TREE_MESH")) {
        TreeMesh =MeshResource::CreateCylinder(1.0f, 1.0f, 18, false);
        Mat4x4 TransToMat;
        TransToMat.affineTransformation(
            Vec3(1.0f,1.0f,1.0f),
            Vec3(0.0f, 0.0f, 0.0f),
            Vec3(0.0f, 0.0f, 0.0f),
            Vec3(0.0f, 0.0f, 0.0f)
            );
        TreeMesh->SetMeshToTransformMatrix(TransToMat);
        TreeMesh->SetUseMeshToTransformMatrix(true);
        App::GetApp()->RegisterResource(L"TREE_MESH", TreeMesh);
    }
    else {
        TreeMesh = App::GetApp()->GetResource<MeshResource>(L"TREE_MESH");
    }
    //このメッシュ特有のテクスチャとして登録
    TreeMesh->SetTextureResource(L"BROWN_TX");


    if (!App::GetApp()->CheckResource<MeshResource>(L"LEAF_MESH_1")) {
        LeafMesh1 = MeshResource::CreateCone(1.0f, 1.0f,18,false);
        Mat4x4 TransToMat;
        TransToMat.affineTransformation(
            Vec3(3.0f, 0.5f, 3.0f),
            Vec3(0.0f, 0.0f, 0.0f),
            Vec3(0.0f, 0.0f, 0.0f),
            Vec3(0.0f, 0.4f, 0.0f)
        );
        LeafMesh1->SetMeshToTransformMatrix(TransToMat);
        LeafMesh1->SetUseMeshToTransformMatrix(true);
        App::GetApp()->RegisterResource(L"LEAF_MESH_1", LeafMesh1);
    }
    else {
        LeafMesh1 = App::GetApp()->GetResource<MeshResource>(L"LEAF_MESH_1");
    }
    //このメッシュ特有のテクスチャとして登録
    LeafMesh1->SetTextureResource(L"GREEN_TX");

    if (!App::GetApp()->CheckResource<MeshResource>(L"LEAF_MESH_2")) {
        LeafMesh2 = MeshResource::CreateCone(1.0f, 1.0f, 18, false);
        Mat4x4 TransToMat;
        TransToMat.affineTransformation(
            Vec3(2.0f, 0.5f, 2.0f),
            Vec3(0.0f, 0.0f, 0.0f),
            Vec3(0.0f, 0.0f, 0.0f),
            Vec3(0.0f, 0.7f, 0.0f)
        );
        LeafMesh2->SetMeshToTransformMatrix(TransToMat);
        LeafMesh2->SetUseMeshToTransformMatrix(true);
        App::GetApp()->RegisterResource(L"LEAF_MESH_2", LeafMesh2);
    }
    else {
        LeafMesh2 = App::GetApp()->GetResource<MeshResource>(L"LEAF_MESH_2");
    }
    //このメッシュ特有のテクスチャとして登録
    LeafMesh2->SetTextureResource(L"GREEN_TX");

    //このオブジェクトはマルチメッシュを使う
    m_MultiMeshResource = ObjectFactory::Create<MultiMeshResource>();
    m_MultiMeshResource->AddMesh(TreeMesh);
    m_MultiMeshResource->AddMesh(LeafMesh1);
    m_MultiMeshResource->AddMesh(LeafMesh2);

    //影をつける
    auto ShadowPtr = AddComponent<Shadowmap>();
    ShadowPtr->SetMultiMeshResource(m_MultiMeshResource);

    //描画コンポーネントの設定
    auto PtrDraw = AddComponent<BcPNTStaticDraw>();
    //描画するマルチメッシュを設定
    PtrDraw->SetMultiMeshResource(m_MultiMeshResource);

}
 マルチメッシュを作成する前にそのンンスタンスのTransformを確定させます。衝突判定も設定します。
 複数のメッシュをまとめるわけですが衝突判定は一つです。ここではCollisionCapsuleを使用しています。
 またマルチメッシュに参加させる単体のメッシュを作成します。こちらは最初のインスタンスが作成されたときにリソース化して、ほかのインスタンスが再利用できるようにします。
 以下は、上部の葉っぱのメッシュに関する処理です。
    if (!App::GetApp()->CheckResource<MeshResource>(L"LEAF_MESH_2")) {
        LeafMesh2 = MeshResource::CreateCone(1.0f, 1.0f, 18, false);
        Mat4x4 TransToMat;
        TransToMat.affineTransformation(
            Vec3(2.0f, 0.5f, 2.0f),
            Vec3(0.0f, 0.0f, 0.0f),
            Vec3(0.0f, 0.0f, 0.0f),
            Vec3(0.0f, 0.7f, 0.0f)
        );
        LeafMesh2->SetMeshToTransformMatrix(TransToMat);
        LeafMesh2->SetUseMeshToTransformMatrix(true);
        App::GetApp()->RegisterResource(L"LEAF_MESH_2", LeafMesh2);
    }
    else {
        LeafMesh2 = App::GetApp()->GetResource<MeshResource>(L"LEAF_MESH_2");
    }
    //このメッシュ特有のテクスチャとして登録
    LeafMesh2->SetTextureResource(L"GREEN_TX");
 このようにメッシュがリソースに登録がないかチェックして、なければメッシュを作成するという処理をしています。
 メッシュリソースであるMeshResourceクラスはそのメッシュ独自のMeshToTransformMatrixを設定することができます。前者はワールド行列に対する相対的な行列です。葉っぱは上下どちらもコーンというプリミティブを使用していますので、ワールド行列に対する大きさや位置関係などを設定することで、上下の葉っぱを表現します。
 MeshToTransformMatrixを設定した後
        LeafMesh2->SetMeshToTransformMatrix(TransToMat);
        LeafMesh2->SetUseMeshToTransformMatrix(true);
 とすれば、その相対行列を使用するようになります。
 テクスチャも同様、そのメッシュ独自のものを設定できます。
    //このメッシュ特有のテクスチャとして登録
    LeafMesh2->SetTextureResource(L"GREEN_TX");
 とします。L"GREEN_TX"というのはあらかじめScene.cppでリソース化されたテクスチャです。
 これらの部品を作成したら、それらの部品をまとめます。
    //このオブジェクトはマルチメッシュを使う
    m_MultiMeshResource = ObjectFactory::Create<MultiMeshResource>();
    m_MultiMeshResource->AddMesh(TreeMesh);
    m_MultiMeshResource->AddMesh(LeafMesh1);
    m_MultiMeshResource->AddMesh(LeafMesh2);
 このようにm_MultiMeshResourceを構築してそこに部品(各メッシュ)を登録します。
 MultiMeshResourceリソース登録も可能ですが、前述したように、単体のメッシュと違い、内部に頂点バッファなどのメモリを圧迫するデータは持ちませんので、リソース登録しなくても問題ありません。

 最後に、シャドウマップと描画コンポーネントへの登録を行います。
    //影をつける
    auto ShadowPtr = AddComponent<Shadowmap>();
    ShadowPtr->SetMultiMeshResource(m_MultiMeshResource);

    //描画コンポーネントの設定
    auto PtrDraw = AddComponent<BcPNTStaticDraw>();
    //描画するマルチメッシュを設定
    PtrDraw->SetMultiMeshResource(m_MultiMeshResource);
 シャドウマップ、描画コンポーネント両方ともSetMultiMeshResourceメンバ関数で、マルチメッシュリソースの登録を行います。

 GameStageクラスでは、複数のTreeクラスインスタンスを実装しています。以下がその部分です。
    //木の作成
    void GameStage::CreateTrees() {
        for (float x = -22.0f; x < 22.0f; x += 4.0f) {
            for (float z = -22.0f; z < 22.0f; z += 4.0f) {
                AddGameObject<Tree>(Vec3(x, 0.5f, z));
            }
        }
    }
 構造さえわかれば、いろんな組み合わせが可能かと思います。
 BaseCrossで作成できるプリミティブはライブラリ中MeshHelper.hDeviceResources.hMeshResourceクラスに記述があります。確認してみましょう。