モデル表示(1)


 1.はじめに…
ずっと板ポリもあれなので,今回はPMDフォーマットを表示させてみます。
モデルデータのプロ生ちゃんはプログラミング生放送様(http://pronama.azurewebsites.net/pronama/download/)よりダウンロードして使用させて頂いております。
モデルデータは利用ガイドライン(http://pronama.azurewebsites.net/pronama/guideline/)を読んだうえで,各自でダウンロードしてください。





 2.PMDフォーマット
今回はMikuMikuDance(MMD)で使用されているPolygon Model Data(PMD)フォーマットを読み込んでみます。
とりあえず表示だけやりたかったので,後でPBRに移行するというのもあるのでライティングはなしで,テクスチャカラーを表示しているだけの手抜きです。

さて,PMDフォーマットですが"通りすがりの記憶"さんのページに詳しいものがまとまっています。
まず,ファイルの構成は下記のようになっているようです。
PMD ファイル
 ・ヘッダ
 ・頂点リスト
 ・面リスト
 ・材質リスト
 ・ボーンリスト
 ・IKリスト
 ・表情リスト
 ・表情枠用表示リスト
 ・ボーン枠用枠名リスト
 ・ボーン枠用表示リスト
 --- 拡張部分 ---
 ・ヘッダ(英語)
 ・ボーンリスト(英語)
 ・表情リスト(英語)
 ・ボーン枠用枠名リスト(英語)
 ・トゥーンテクスチャリスト
 ・剛体リスト
 ・ジョイントリスト
モデルを表示するだけなら,拡張部分は全くいりませんが後でコリジョンをとりたいなどの場合に剛体リストとジョイントリストを使用することになるので,一応すべて読み込んでおくことにしました。
拡張部分というのが必ず存在するのかどうかわからなかったので,一応EOFになっているかどうかをチェックしながら読み取るようにプログラムを組んでみました。そのため最後の方はちょっとコードが汚いです。

データ構造は下記のように定義しました。
00022:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00023:  // PMD_EDGE_FLAG 
00024:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00025:  enum PMD_EDGE_FLAG
00026:  {
00027:      PMD_EDGE_FLAG_DEFAULT = 0,  //!< 通常.
00028:      PMD_EDGE_FLAG_DISABLE = 1   //!< エッジ無効.
00029:  };
00030:  
00031:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00032:  // PMD_BONE_TYPE enum
00033:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00034:  enum PMD_BONE_TYPE
00035:  {
00036:      PMD_BONE_TYPE_ROTATE                    = 0,        //!< 回転.
00037:      PMD_BONE_TYPE_ROTATE_AND_TARNSLATE      = 1,        //!< 回転と移動.
00038:      PMD_BONE_TYPE_IK                        = 2,        //!< IK
00039:      PMD_BONE_TYPE_UNKNOWN                   = 3,        //!< 不明.
00040:      PMD_BONE_TYPE_UNDER_INFLUENCE_OF_IK     = 4,        //!< IK影響下.
00041:      PMD_BONE_TYPE_UNDER_INFLUENCE_OF_ROTATE = 5,        //!< 回転影響下.
00042:      PMD_BONE_TYPE_TARGET_IK                 = 6,        //!< IK接続先.
00043:      PMD_BONE_TYPE_INVISIBLE                 = 7,        //!< 非表示.
00044:      PMD_BONE_TYPE_TWIST                     = 8,        //!< 捻り.
00045:      PMD_BONE_TYPE_ROTATRY_MOTION            = 9,        //!< 回転運動.
00046:  };
00047:  
00048:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00049:  // PMD_MORPH_TYPE enum
00050:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00051:  enum PMD_MORPH_TYPE
00052:  {
00053:      PMD_MORPH_TYPE_BASE     = 0,        //!< base
00054:      PMD_MORPH_TYPE_EYEBROW  = 1,        //!< 眉.
00055:      PMD_MORPH_TYPE_EYE      = 2,        //!< 目.
00056:      PMD_MORPH_TYPE_LIP      = 3,        //!< 唇.
00057:      PMD_MORPT_TYPE_OTHER    = 4,        //!< その他.
00058:  };
00059:  
00060:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00061:  // PMD_COLLISION_SHAPE_TYPE enum
00062:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00063:  enum PMD_COLLISION_SHAPE_TYPE
00064:  {
00065:      PMD_COLLISION_SHAPE_TYPE_SPHERE  = 0,       //!< 球.
00066:      PMD_COLLISION_SHAPE_TYPE_BOX     = 1,       //!< 箱.
00067:      PMD_COLLISION_SHAPE_TYPE_CAPSULE = 2,       //!< カプセル.
00068:  };
00069:  
00070:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00071:  // PMD_RIGIDBODY_TYPE
00072:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00073:  enum PMD_RIGIDBODY_TYPE
00074:  {
00075:      PMD_RIGIDBODY_TYPE_FLLOW_BONE             = 0,  //!< ボーン追従.
00076:      PMD_RIGIDBODY_TYPE_PHYSICS                = 1,  //!< 物理演算.
00077:      PMD_RIGIDBODY_TYPE_PHYSICS_FLLOW_POSITION = 2,  //!< 物理演算(ボーン位置追従).
00078:  };
00079:  
00080:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00081:  // PMD_HEADER strcture
00082:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00083:  #pragma pack( push, 1 )
00084:  struct PMD_HEADER
00085:  {
00086:      u8      Magic[3];           //!< ファイルマジック "Pmd"
00087:      f32     Version;            //!< ファイルバージョン.
00088:      char8   ModelName[20];      //!< モデル名.
00089:      char8   Comment[256];       //!< コメント.
00090:  };
00091:  #pragma pack( pop )
00092:  
00093:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00094:  // PMD_VERTEX structure
00095:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00096:  #pragma pack( push, 1 )
00097:  struct PMD_VERTEX
00098:  {
00099:      asdx::Vector3   Position;       //!< 位置座標.
00100:      asdx::Vector3   Normal;         //!< 法線ベクトル.
00101:      asdx::Vector2   TexCoord;       //!< テクスチャ座標.
00102:      u16             BoneIndex[2];   //!< ボーン番号. モデル変形(頂点移動)時に影響.
00103:      u8              BoneWeight;     //!< ボーン0へ与える影響度 min: 0, max : 100 (ボーン1への影響度は 100 - BoneWeight).
00104:      u8              EdgeFlag;       //!< エッジフラグ.
00105:  };
00106:  #pragma pack( pop )
00107:  
00108:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00109:  // PMD_MATERIAL structure
00110:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00111:  #pragma pack( push, 1 )
00112:  struct PMD_MATERIAL
00113:  {
00114:      asdx::Vector3   Diffuse;            //!< 拡散反射
00115:      f32             Alpha;              //!< 透過度.
00116:      f32             Power;              //!< 鏡面反射強度.
00117:      asdx::Vector3   Specular;           //!< 鏡面反射.
00118:      asdx::Vector3   Emissive;           //!< 自己照明.
00119:      u8              ToonIndex;          //!< トゥーンテクスチャ番号.
00120:      u8              VisualFlag;         //!< 輪郭. 影
00121:      u32             VertexCount;        //!< 頂点数.
00122:      char8           TextureName[20];    //!< テクスチャファイル名(非NULL終端であることに注意).
00123:  };
00124:  #pragma pack( pop )
00125:  
00126:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00127:  // PMD_BONE structure
00128:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00129:  #pragma pack( push, 1 )
00130:  struct PMD_BONE
00131:  {
00132:      char8           Name[20];           //!< ボーン名.
00133:      u16             ParentIndex;        //!< 親ボーンの番号(親がいない場合は 0xFFFF).
00134:      u16             TailIndex;          //!< 末尾のボーン番号
00135:      u8              BoneType;           //!< ボーンの種類.
00136:      u16             IKBoneIndex;        //!< IKボーン番号(影響IKボーン。無い場合は0)
00137:      asdx::Vector3   Position;           //!< ボーンのヘッド位置.
00138:  };
00139:  #pragma pack( pop )
00140:  
00141:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00142:  // PMD_IK structure
00143:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00144:  #pragma pack( push, 1 )
00145:  struct PMD_IK
00146:  {
00147:      u16     BoneIndex;          //!< IKボーン番号.
00148:      u16     TargetBoneIndex;    //!< IKターゲットボーン番号(IKボーンが最初に接続するボーン)
00149:      u8      ChainCount;         //!< IKチェーンの長さ(子供の数)
00150:      u16     RecursiveCount;     //!< 再帰演算回数.
00151:      f32     ControlWeight;      //!< 演算1回当たりの制限角度.
00152:  };
00153:  #pragma pack( pop )
00154:  
00155:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00156:  // PMD_MORPH_VERTEX structure
00157:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00158:  struct PMD_MORPH_VERTEX
00159:  {
00160:      u32           Index;        //!< 表情用の頂点番号.
00161:      asdx::Vector3 Position;     //!< 表情用の頂点の位置座標(頂点自体の座標).
00162:  };
00163:  
00164:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00165:  // PMD_MORPH structure
00166:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00167:  #pragma pack( push, 1 )
00168:  struct PMD_MORPH
00169:  {
00170:      char8   Name[20];       //!< 表情名.
00171:      u32     VertexCount;    //!< 表情用の頂点数.
00172:      u8      Type;           //!< 表情の種類.
00173:  };
00174:  #pragma pack( pop )
00175:  
00176:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00177:  // PMD_BONE_LABEL_INDEX structure
00178:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00179:  #pragma pack( push, 1 )
00180:  struct PMD_BONE_LABEL_INDEX
00181:  {
00182:      u16     BoneIndex;      //!< 表示枠用ボーン番号.
00183:      u8      FrameIndex;     //!< 表示枠番号
00184:  };
00185:  #pragma pack( pop )
00186:  
00187:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00188:  // PMD_LOCALIZE_HEADER structure
00189:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00190:  #pragma pack( push, 1 )
00191:  struct PMD_LOCALIZE_HEADER
00192:  {
00193:      u8      LocalizeFlag;       //!< ローカライズフラグ(1 : 英名対応あり)
00194:      char8   ModelName[20];      //!< 英語名モデル.
00195:      char8   Comment[256];       //!< 英語コメント.
00196:  };
00197:  #pragma pack( pop )
00198:  
00199:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00200:  // PMD_TOON_TEXTURE_LIST structure
00201:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00202:  #pragma pack( push, 1 )
00203:  struct PMD_TOON_TEXTURE_LIST
00204:  {
00205:      char8 FileName[10][100];    //!< トゥーンテクスチャファイル名.
00206:  };
00207:  #pragma pack( pop )
00208:  
00209:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00210:  // PMD_RIGIDBODY structure
00211:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00212:  #pragma pack( push, 1 )
00213:  struct PMD_RIGIDBODY
00214:  {
00215:      char8           Name[20];               //!< 名前. (例:頭)
00216:      u16             RelationBoneIndex;      //!< 関連ボーン番号.
00217:      u8              GroupIndex;             //!< グループ番号.
00218:      u16             GroupTarget;            //!< グループ対象.
00219:      u8              ShapeType;              //!< 形状.
00220:      asdx::Vector3   ShapeSize;              //!< 形状サイズ.
00221:      asdx::Vector3   Position;               //!< 位置座標.
00222:      asdx::Vector3   Angle;                  //!< 回転角(ラジアン).
00223:      f32             Mass;                   //!< 質量.
00224:      f32             DampingTranslate;       //!< 移動減衰.
00225:      f32             DampingRotation;        //!< 回転減衰.
00226:      f32             Elastictiy;             //!< 反発力.
00227:      f32             Friction;               //!< 摩擦力.
00228:      u8              Type;                   //!< タイプ.
00229:  };
00230:  #pragma pack( pop )
00231:  
00232:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00233:  // PMD_PHYSICS_JOINT structure
00234:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00235:  #pragma pack( push, 1 )
00236:  struct PMD_PHYSICS_JOINT
00237:  {
00238:      char8           Name[20];           //!< 名前.
00239:      u32             RigidBodyA;         //!< 剛体A.
00240:      u32             RigidBodyB;         //!< 剛体B.
00241:      asdx::Vector3   Position;           //!< 位置座標.
00242:      asdx::Vector3   Angle;              //!< 回転角.
00243:      asdx::Vector3   LimitPosition[2];   //!< 移動制限.
00244:      asdx::Vector3   LimitAngle[2];      //!< 回転制限.
00245:      asdx::Vector3   SpringPosition;     //!< ばね 位置座標.
00246:      asdx::Vector3   SpringAngle;        //!< ばな 回転角.
00247:  };
00248:  #pragma pack( pop )
00249:  
00250:  
00251:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00252:  // ResPmdIK structure
00253:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00254:  struct ResPmdIK
00255:  {
00256:      u16                 BoneIndex;          //!< IKボーン番号.
00257:      u16                 TargetBoneIndex;    //!< IKターゲットボーン番号.
00258:      u16                 RecursiveCount;     //!< 再帰演算回数.
00259:      f32                 ControlWeight;      //!< 演算1会当たりの制限角度.
00260:      std::vector<u16>    ChildBoneIndices;   //!< IK影響下のボーン番号.
00261:  };
00262:  
00263:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00264:  // ResPmdMorph structure
00265:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00266:  struct ResPmdMorph
00267:  {
00268:      std::string                     Name;       //!< 名前.
00269:      u8                              Type;       //!< 表情タイプ.
00270:      std::vector<PMD_MORPH_VERTEX>   Vertices;   //!< 表情用の頂点データ.
00271:  };
00272:  
00273:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00274:  // ResPmd class
00275:  ///////////////////////////////////////////////////////////////////////////////////////////////////
00276:  class ResPmd : public ILoadable, public IDisposable
00277:  {
00278:      //=============================================================================================
00279:      // list of friend classes and methods.
00280:      //=============================================================================================
00281:      /* NOTHING /*
00282:  
00283:  public:
00284:      //=============================================================================================
00285:      // public variables.
00286:      //=============================================================================================
00287:      std::string                         Name;                   //!< モデル名.
00288:      std::string                         Comment;                //!< コメント.
00289:      f32                                 Version;                //!< バージョン.
00290:      std::vector<PMD_VERTEX>             Vertices;               //!< 頂点データ.
00291:      std::vector<u16>                    Indices;                //!< 頂点インデックスデータ.
00292:      std::vector<PMD_MATERIAL>           Materials;              //!< マテリアルデータ.
00293:      std::vector<PMD_BONE>               Bones;                  //!< ボーンデータ.
00294:      std::vector<ResPmdIK>               IKs;                    //!< IKデータ.
00295:      std::vector<ResPmdMorph>            Morphes;                //!< モーフデータ.
00296:      std::vector<u16>                    MorphLabelIndices;      //!< 表情枠用表示リスト.
00297:      std::vector<std::string>            BoneLabels;             //!< ボーン枠用枠名リスト.
00298:      std::vector<PMD_BONE_LABEL_INDEX>   BoneLabelIndices;       //!< ボーン枠用表示リスおt.
00299:      PMD_TOON_TEXTURE_LIST               ToonTextureList;        //!< トゥーンテクスチャリスト.
00300:      std::vector<PMD_RIGIDBODY>          RigidBodies;            //!< 剛体データ.
00301:      std::vector<PMD_PHYSICS_JOINT>      Joints;                 //!< ジョイントデータ.
00302:  
00303:      //=============================================================================================
00304:      // public methods.
00305:      //=============================================================================================
00306:  
00307:      //---------------------------------------------------------------------------------------------
00308:      //! @brief      コンストラクタです
00309:      //---------------------------------------------------------------------------------------------
00310:      ResPmd();
00311:  
00312:      //---------------------------------------------------------------------------------------------
00313:      //! @brief      デストラクタです.
00314:      //---------------------------------------------------------------------------------------------
00315:      virtual ~ResPmd();
00316:  
00317:      //---------------------------------------------------------------------------------------------
00318:      //! @brief      ファイルから読み込みを行います.
00319:      //!
00320:      //! @param[in]      filename        ファイル名です.
00321:      //! @retval true    読み込みに成功.
00322:      //! @retval false   読み込みに失敗.
00323:      //---------------------------------------------------------------------------------------------
00324:      bool Load( const char16* filename ) override;
00325:  
00326:      //---------------------------------------------------------------------------------------------
00327:      //! @brief      破棄処理を行います.
00328:      //---------------------------------------------------------------------------------------------
00329:      void Dispose() override;
00330:  };
非常に読み込みやすいフォーマットになっているので,プログラムの方はファイルを開いてデータ構造通りにfread()していけば簡単に読み込めます。
実装は下記のような感じです。
00033:  //-------------------------------------------------------------------------------------------------
00034:  //      ファイルから読み込みを行います.
00035:  //-------------------------------------------------------------------------------------------------
00036:  bool ResPmd::Load( const char16* filename )
00037:  {
00038:      if ( filename == nullptr )
00039:      {
00040:          ELOG( "Error : Invalid Argument." );
00041:          return false;
00042:      }
00043:  
00044:      FILE* pFile;
00045:      auto err = _wfopen_s( &pFile, filename, L"rb" );
00046:      if ( err != 0 )
00047:      {
00048:          ELOG( "Error : File Open Failed. filename = %s", filename );
00049:          return false;
00050:      }
00051:  
00052:      // ヘッダ読み込み.
00053:      PMD_HEADER header;
00054:      fread( &header, sizeof(header), 1, pFile );
00055:  
00056:      // ファイルマジックをチェック.
00057:      if ( header.Magic[0] != 'P' || header.Magic[1] != 'm' || header.Magic[2] != 'd' )
00058:      {
00059:          ELOG( "Error : Invalid File. filename = %s", filename );
00060:          return false;
00061:      }
00062:  
00063:      Name    = header.ModelName;
00064:      Version = header.Version;
00065:      Comment = header.Comment;
00066:  
00067:      // 頂点リストを読み込み.
00068:      {
00069:          u32 vertexCount = 0;
00070:          fread( &vertexCount, sizeof(vertexCount), 1, pFile );
00071:  
00072:          Vertices.resize( vertexCount );
00073:  
00074:          for( u32 i=0; i<vertexCount; ++i )
00075:          { fread( &Vertices[i], sizeof(PMD_VERTEX), 1, pFile ); }
00076:      }
00077:  
00078:      // 頂点インデックスを読み込み.
00079:      {
00080:          u32 vertexCount = 0;
00081:          fread( &vertexCount, sizeof(vertexCount), 1, pFile );
00082:  
00083:          Indices.resize(vertexCount);
00084:  
00085:          for( u32 i=0; i<vertexCount; ++i )
00086:          { fread( &Indices[i], sizeof(u16), 1, pFile ); }
00087:      }
00088:  
00089:      // マテリアルを読み込み.
00090:      {
00091:          u32 materialCount = 0;
00092:          fread( &materialCount, sizeof(materialCount), 1, pFile );
00093:  
00094:          Materials.resize( materialCount );
00095:  
00096:          for( u32 i=0; i<materialCount; ++i )
00097:          { fread( &Materials[i], sizeof(PMD_MATERIAL), 1, pFile ); }
00098:      }
00099:  
00100:      // ボーンデータを読み込み.
00101:      {
00102:          u16 boneCount = 0;
00103:          fread( &boneCount, sizeof(boneCount), 1, pFile );
00104:  
00105:          Bones.resize( boneCount );
00106:  
00107:          for( u16 i=0; i<boneCount; ++i )
00108:          { fread( &Bones[i], sizeof(PMD_BONE), 1, pFile ); }
00109:      }
00110:  
00111:      // IKデータを読み込み.
00112:      {
00113:          u16 count = 0;
00114:          fread( &count, sizeof(count), 1, pFile );
00115:  
00116:          IKs.resize( count );
00117:  
00118:          for( u32 i=0; i<count; ++i )
00119:          {
00120:              PMD_IK data;
00121:              fread( &data, sizeof(data), 1, pFile );
00122:  
00123:              IKs[i].BoneIndex       = data.BoneIndex;
00124:              IKs[i].TargetBoneIndex = data.TargetBoneIndex;
00125:              IKs[i].RecursiveCount  = data.RecursiveCount;
00126:              IKs[i].ControlWeight   = data.ControlWeight;
00127:  
00128:              IKs[i].ChildBoneIndices.resize( data.ChainCount );
00129:  
00130:              for( u32 j=0; j<data.ChainCount; ++j )
00131:              { fread( &IKs[i].ChildBoneIndices[j], sizeof(u16), 1, pFile ); }
00132:          }
00133:      }
00134:  
00135:      // モーフデータを読み込み.
00136:      {
00137:          u16 morphCount = 0;
00138:          fread( &morphCount, sizeof(morphCount), 1, pFile );
00139:  
00140:          Morphes.resize( morphCount );
00141:  
00142:          for( u16 i=0; i<morphCount; ++i )
00143:          {
00144:              PMD_MORPH morph;
00145:              fread( &morph, sizeof(morph), 1, pFile );
00146:  
00147:              Morphes[i].Name = morph.Name;
00148:              Morphes[i].Type = morph.Type;
00149:  
00150:              Morphes[i].Vertices.resize( morph.VertexCount );
00151:  
00152:              for( u32 j=0; j<morph.VertexCount; ++j )
00153:              { fread( &Morphes[i].Vertices[j], sizeof(PMD_MORPH_VERTEX), 1, pFile ); }
00154:          }
00155:      }
00156:  
00157:      // 表情枠用表示リスト.
00158:      {
00159:          u8 count = 0;
00160:          fread( &count, sizeof(count), 1, pFile );
00161:  
00162:          MorphLabelIndices.resize( count );
00163:  
00164:          for( u8 i=0; i<count; ++i )
00165:          { fread( &MorphLabelIndices[i], sizeof(u16), 1, pFile ); }
00166:      }
00167:  
00168:      // ボーン枠用枠名リスト.
00169:      {
00170:          u8 count = 0;
00171:          fread( &count, sizeof(count), 1, pFile );
00172:  
00173:          BoneLabels.resize( count );
00174:  
00175:          for( u32 i=0; i<count; ++i )
00176:          {
00177:              char name[50];
00178:              fread( name, sizeof(char), 50, pFile );
00179:  
00180:              BoneLabels[i] = name;
00181:          }
00182:      }
00183:  
00184:      // ボーン枠用表示リスト.
00185:      {
00186:          u32 count = 0;
00187:          fread( &count, sizeof(count), 1, pFile );
00188:  
00189:          BoneLabelIndices.resize( count );
00190:  
00191:          for( u32 i=0; i<count; ++i )
00192:          { fread( &BoneLabelIndices[i], sizeof(PMD_BONE_LABEL_INDEX), 1, pFile ); }
00193:      }
00194:  
00195:      // 拡張データ ローカライズデータ.
00196:      if ( feof( pFile ) == 0 )
00197:      {
00198:          PMD_LOCALIZE_HEADER headerEn;
00199:          fread( &headerEn, sizeof(headerEn), 1, pFile );
00200:  
00201:          if ( headerEn.LocalizeFlag == 0x1 )
00202:          {
00203:              //std::string modelName = headerEn.ModelName;
00204:              //std::string comment = headerEn.Comment;
00205:              //ILOGA( "ModelName[EN] : %s", modelName.c_str() );
00206:              //ILOGA( "Comment[EN] : %s", comment.c_str() );
00207:  
00208:              for( size_t i=0; i<Bones.size(); ++i )
00209:              {
00210:                  char boneName[20];
00211:                  fread( boneName, sizeof(char), 20, pFile );
00212:                  //ILOGA( "BoneName[EN] : %s", boneName );
00213:              }
00214:  
00215:              for( size_t i=0; i<Morphes.size() - 1; ++i )
00216:              {
00217:                  char morphName[20];
00218:                  fread( morphName, sizeof(char), 20, pFile );
00219:                  //ILOGA( "MorphName[EN] : %s", morphName );
00220:              }
00221:  
00222:              for( size_t i=0; i<BoneLabels.size(); ++i )
00223:              {
00224:                  char label[50];
00225:                  fread( label, sizeof(char), 50, pFile );
00226:                  //ILOGA( "BoneLabel[EN] : %s", label );
00227:              }
00228:          }
00229:      }
00230:  
00231:      // 拡張データ トゥーンテクスチャリスト.
00232:      if ( feof( pFile ) == 0 )
00233:      {
00234:          fread( &ToonTextureList, sizeof(ToonTextureList), 1, pFile );
00235:      }
00236:  
00237:      // 拡張データ 剛体データ.
00238:      if ( feof( pFile ) == 0 )
00239:      {
00240:          u32 rigidBodyCount = 0;
00241:          fread( &rigidBodyCount, sizeof(rigidBodyCount), 1, pFile );
00242:  
00243:          RigidBodies.resize( rigidBodyCount );
00244:  
00245:          for( u32 i=0; i<rigidBodyCount; ++i )
00246:          { fread( &RigidBodies[i], sizeof(PMD_RIGIDBODY), 1, pFile ); }
00247:      }
00248:  
00249:      // 拡張データ ジョイントリスト.
00250:      if ( feof( pFile ) == 0 )
00251:      {
00252:          u32 jointCount = 0;
00253:          fread( &jointCount, sizeof(jointCount), 1, pFile );
00254:  
00255:          Joints.resize( jointCount );
00256:  
00257:          for( u32 i=0; i<jointCount; ++i )
00258:          { fread( &Joints[i], sizeof(PMD_PHYSICS_JOINT), 1, pFile ); }
00259:      }
00260:  
00261:      // ファイルを閉じる.
00262:      fclose( pFile );
00263:  
00264:      // 正常終了.
00265:      return true;
00266:  }
色々なデータで読み込みを試していないので,拡張データのあたりで読み込み失敗とかエラーが発生するかもしれないので,その場合はうまく直してください。
一応これでPMDデータ自体は読み込みできるはずです。"初音ミク.pmd"と"プロ生ちゃん.pmd"でしか検証していませんが…。


 3.描画してみる
さて実際に読み込んだデータを表示してみます。プロ生ちゃんのテクスチャデータがすべて*.pngで作られていますが,WICの読み込みを移植していない関係で読み込めません。 そこで,今回は*.pngデータをすべて*.ddsに変換して読み込みを行いました。PMD EditorとDirectX Texture Toolを使って編集を行いました。

プログラムの解説に入ります。まずは先ほど実装した読み込みプログラムを使用して,PMDデータの読み込みを行い,テクスチャリストとテクスチャIDリストを構築します。
PMDのTextureNameには"test.dds*a.spa"のような感じで,テクスチャファイル名とスフィアファイル名が一緒に入る仕様のようなので,テクスチャファイル名のみを取り出します。これは505行目付近で行っています。
ピクセルシェーダでテクスチャマップがあるかどうかを判断すると条件分岐が出てきてしまうため,処理が重くなる可能性があります。そこで,PMDのマテリアルデータにテクスチャがない箇所はダミーのテクスチャを割り当てし,これをピクセルシェーダ内でフェッチするようにします。 もちろんダミーテクスチャをフェッチしても問題がないように,ダミーのテクスチャは全部白色(1.0f, 1.0f, 1.0f, 1.0f)を入れてあります。
00470:      {
00471:          std::wstring path;
00472:          if ( !asdx::SearchFilePathW( L"res/pronama/プロ生ちゃん.pmd", path ) )
00473:          {
00474:              ELOG( "Error : SearchFilePath() Failed. " );
00475:              return false;
00476:          }
00477:  
00478:          if ( !m_ModelData.Load( path.c_str() ) )
00479:          {
00480:              ELOG( "Error : ResPmd::Load() Failed." );
00481:              return false;
00482:          }
00483:      }
00484:  
00485:      std::vector<s32> textureIdList;
00486:      std::vector<std::wstring> textureNameList;
00487:      {
00488:          std::string path;
00489:          textureIdList.resize( m_ModelData.Materials.size() );
00490:  
00491:          {
00492:              std::wstring wpath;
00493:              if ( asdx::SearchFilePathW( L"res/dummy.dds", wpath ) )
00494:              {
00495:                  textureNameList.push_back( wpath.c_str() );
00496:              }
00497:          }
00498:  
00499:          for( size_t i=0; i<m_ModelData.Materials.size(); ++i )
00500:          {
00501:              std::string temp = m_ModelData.Materials[i].TextureName;
00502:              if ( temp.empty() )
00503:              { continue; }
00504:  
00505:              // スフィアマップ名は取り除く.
00506:              size_t idx = temp.find('*');
00507:              if ( idx != std::string::npos )
00508:              { temp = temp.substr(0, idx); }
00509:  
00510:              std::string input = "res/pronama/" + temp;
00511:  
00512:              // 存在確認.
00513:              if ( !asdx::SearchFilePathA( input.c_str(), path ) )
00514:              {
00515:                  textureIdList[i] = 0; // 見つからなかったらダミーテクスチャを使用.
00516:                  continue;
00517:              }
00518:  
00519:              // ワイド文字に変換.
00520:              auto filePath = asdx::ToStringW( path );
00521:  
00522:              // リストを検索.
00523:              auto isFind = false;
00524:              for( size_t j=0; j<textureNameList.size(); ++j )
00525:              {
00526:                  if ( textureNameList[j] == filePath )
00527:                  {
00528:                      isFind = true;
00529:                      textureIdList[i] = u32(j); 
00530:                      break;
00531:                  }
00532:              }
00533:  
00534:              // 検索に引っかからなかったら追加.
00535:              if ( !isFind )
00536:              { 
00537:                  textureNameList.push_back( filePath );
00538:                  textureIdList[i] = u32( textureNameList.size() - 1 );
00539:              }
00540:          }
00541:      }
続いて,PMDデータから頂点バッファとインデックスバッファ,シェーダに送るための定数バッファなどを構築していきます。
00690:      // CBV・SRV・UAV用ディスクリプターヒープを生成.
00691:      {
00692:          D3D12_DESCRIPTOR_HEAP_DESC desc = {};
00693:          desc.NumDescriptors = 1
00694:              + static_cast<u32>(m_ModelData.Materials.size())
00695:              + static_cast<u32>(textureNameList.size());
00696:          desc.Flags          = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
00697:          desc.Type           = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
00698:  
00699:          if ( !m_Heap[DESC_HEAP_BUFFER].Init( m_pDevice.GetPtr(), &desc ) )
00700:          {
00701:              ELOG( "Error : DescHeap::Init() Failed." );
00702:              return false;
00703:          }
00704:      }
00705:  
00706:      // 頂点バッファの生成.
00707:      {
00708:          if ( !m_ModelVB.Init( 
00709:              m_pDevice.GetPtr(), 
00710:              sizeof(asdx::PMD_VERTEX) * m_ModelData.Vertices.size(),
00711:              sizeof(asdx::PMD_VERTEX),
00712:              &m_ModelData.Vertices[0] ) )
00713:          {
00714:              ELOG( "Error : VertexBuffer::Init() Failed." );
00715:              return false;
00716:          }
00717:      }
00718:  
00719:      // インデックスバッファの生成.
00720:      {
00721:          if ( !m_ModelIB.Init(
00722:              m_pDevice.GetPtr(),
00723:              sizeof(u16) * m_ModelData.Indices.size(),
00724:              DXGI_FORMAT_R16_UINT,
00725:              &m_ModelData.Indices[0] ) )
00726:          {
00727:              ELOG( "Error : IndexBuffer::Init() Faild." );
00728:              return false;
00729:          }
00730:      }
00731:  
00732:      // マテリアルバッファの生成.
00733:      {
00734:          if ( !m_ModelMB.Init( 
00735:              m_pDevice.GetPtr(),
00736:              sizeof(ResMaterialBuffer) * m_ModelData.Materials.size() ) )
00737:          {
00738:              ELOG( "Error : ConstantBuffer::Init() Failed." );
00739:              return false;
00740:          }
00741:  
00742:          u32 size = static_cast<u32>(sizeof(ResMaterialBuffer));
00743:  
00744:          // 定数バッファビューの設定.
00745:          D3D12_CONSTANT_BUFFER_VIEW_DESC bufferDesc = {};
00746:          bufferDesc.BufferLocation = m_ModelMB->GetGPUVirtualAddress();
00747:          bufferDesc.SizeInBytes    = size;
00748:  
00749:          u32 offset = 0;
00750:          for( size_t i=0; i<m_ModelData.Materials.size(); ++i )
00751:          {
00752:              m_ModelMB.Update( &m_ModelData.Materials[i], size, offset );
00753:  
00754:              m_pDevice->CreateConstantBufferView( &bufferDesc, m_Heap[DESC_HEAP_BUFFER].GetHandleCPU(1 + u32(i)) );
00755:              bufferDesc.BufferLocation += size;
00756:              offset += size;
00757:          }
00758:      }
頂点バッファ・インデックスバッファ定数バッファはプログラム的にお決まりパターンになるのでラッパークラスを書きました。
頂点バッファの初期化処理は下記のようになります。
00037:  //-------------------------------------------------------------------------------------------------
00038:  //      初期化処理を行います.
00039:  //-------------------------------------------------------------------------------------------------
00040:  bool VertexBuffer::Init( ID3D12Device* pDevice, u64 size, u32 stride, const void* pVertices )
00041:  {
00042:      if ( pDevice == nullptr || pVertices == nullptr || size == 0 || stride == 0 )
00043:      {
00044:          ELOG( "Error : Invalid Arguments." );
00045:          return false;
00046:      }
00047:  
00048:      D3D12_HEAP_PROPERTIES props = {
00049:          D3D12_HEAP_TYPE_UPLOAD,
00050:          D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
00051:          D3D12_MEMORY_POOL_UNKNOWN,
00052:          1,
00053:          1
00054:      };
00055:  
00056:      D3D12_RESOURCE_DESC desc = {
00057:          D3D12_RESOURCE_DIMENSION_BUFFER,
00058:          0,
00059:          size,
00060:          1,
00061:          1,
00062:          1,
00063:          DXGI_FORMAT_UNKNOWN,
00064:          { 1, 0 },
00065:          D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
00066:          D3D12_RESOURCE_FLAG_NONE
00067:      };
00068:  
00069:      auto hr = pDevice->CreateCommittedResource(
00070:          &props,
00071:          D3D12_HEAP_FLAG_NONE,
00072:          &desc,
00073:          D3D12_RESOURCE_STATE_GENERIC_READ,
00074:          nullptr,
00075:          IID_PPV_ARGS(m_Resource.GetAddress()));
00076:      if ( FAILED( hr ) )
00077:      {
00078:          ELOG( "Error : ID3D12Device::CreateCommittedResource() Failed." );
00079:          return false;
00080:      }
00081:  
00082:      u8* pDst;
00083:      hr = m_Resource->Map( 0, nullptr, reinterpret_cast<void**>( &pDst ) );
00084:      if ( FAILED( hr ) )
00085:      {
00086:          ELOG( "Error : ID3D12Resource::Map() Failed." );
00087:          return false;
00088:      }
00089:  
00090:      memcpy( pDst, pVertices, size_t(size) );
00091:  
00092:      m_Resource->Unmap( 0, nullptr );
00093:  
00094:      m_View.BufferLocation = m_Resource->GetGPUVirtualAddress();
00095:      m_View.SizeInBytes    = static_cast<u32>(size);
00096:      m_View.StrideInBytes  = stride;
00097:  
00098:      return true;
00099:  }
インデックスバッファの初期化処理は下記になります。
00037:  //-------------------------------------------------------------------------------------------------
00038:  //      初期化処理を行います.
00039:  //-------------------------------------------------------------------------------------------------
00040:  bool IndexBuffer::Init( ID3D12Device* pDevice, u64 size, DXGI_FORMAT format, const void* pIndices )
00041:  {
00042:      if ( pDevice == nullptr || pIndices == nullptr || size == 0 )
00043:      {
00044:          ELOG( "Error : Invalid Argument." );
00045:          return false;
00046:      }
00047:  
00048:      D3D12_HEAP_PROPERTIES props = {
00049:        D3D12_HEAP_TYPE_UPLOAD,
00050:        D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
00051:        D3D12_MEMORY_POOL_UNKNOWN,
00052:        1,
00053:        1
00054:      };
00055:  
00056:      D3D12_RESOURCE_DESC desc = {
00057:          D3D12_RESOURCE_DIMENSION_BUFFER,
00058:          0,
00059:          size,
00060:          1,
00061:          1,
00062:          1,
00063:          DXGI_FORMAT_UNKNOWN,
00064:          { 1, 0 },
00065:          D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
00066:          D3D12_RESOURCE_FLAG_NONE
00067:      };
00068:  
00069:      auto hr = pDevice->CreateCommittedResource( 
00070:          &props,
00071:          D3D12_HEAP_FLAG_NONE,
00072:          &desc,
00073:          D3D12_RESOURCE_STATE_GENERIC_READ,
00074:          nullptr,
00075:          IID_PPV_ARGS(m_Resource.GetAddress()));
00076:      if ( FAILED( hr ) )
00077:      {
00078:          ELOG( "Error : ID3D12Device::CreateCommittedResource() Failed." );
00079:          return false;
00080:      }
00081:  
00082:      u8* pDst;
00083:      hr = m_Resource->Map( 0, nullptr, reinterpret_cast<void**>( &pDst ) );
00084:      if ( FAILED( hr ) )
00085:      {
00086:          ELOG( "Error : ID3D12Resource::Map() Failed." );
00087:          return false;
00088:      }
00089:  
00090:      memcpy( pDst, pIndices, size_t(size) );
00091:  
00092:      m_Resource->Unmap( 0, nullptr );
00093:  
00094:      m_View.BufferLocation = m_Resource->GetGPUVirtualAddress();
00095:      m_View.SizeInBytes    = static_cast<u32>( size );
00096:      m_View.Format         = format;
00097:  
00098:      return true;
00099:  }
定数バッファの初期化処理は下記のようになります。
00035:  //-------------------------------------------------------------------------------------------------
00036:  //      初期化処理を行います.
00037:  //-------------------------------------------------------------------------------------------------
00038:  bool ConstantBuffer::Init( ID3D12Device* pDevice, u64 size )
00039:  {
00040:      if ( pDevice == nullptr || size == 0 )
00041:      {
00042:          ELOG( "Error : Invalid Argument." );
00043:          return false;
00044:      }
00045:  
00046:      if ( (size % 256) != 0 )
00047:      {
00048:          ELOG( "Error : ConstantBuffer must be 256 byte alignment." );
00049:          return false;
00050:      }
00051:  
00052:      D3D12_HEAP_PROPERTIES props = {
00053:          D3D12_HEAP_TYPE_UPLOAD,
00054:          D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
00055:          D3D12_MEMORY_POOL_UNKNOWN,
00056:          1,
00057:          1
00058:      };
00059:  
00060:      D3D12_RESOURCE_DESC desc = {
00061:          D3D12_RESOURCE_DIMENSION_BUFFER,
00062:          0,
00063:          size,
00064:          1,
00065:          1,
00066:          1,
00067:          DXGI_FORMAT_UNKNOWN,
00068:          { 1, 0 },
00069:          D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
00070:          D3D12_RESOURCE_FLAG_NONE
00071:      };
00072:  
00073:      auto hr = pDevice->CreateCommittedResource(
00074:          &props,
00075:          D3D12_HEAP_FLAG_NONE,
00076:          &desc,
00077:          D3D12_RESOURCE_STATE_GENERIC_READ,
00078:          nullptr,
00079:          IID_PPV_ARGS(m_Resource.GetAddress()));
00080:      if ( FAILED( hr ) )
00081:      {
00082:          ELOG( "Error : ID3D12Device::CreateCommittedResource() Failed." );
00083:          return false;
00084:      }
00085:  
00086:      hr = m_Resource->Map( 0, nullptr, reinterpret_cast<void**>( &m_pDst ) );
00087:      if ( FAILED( hr ) )
00088:      {
00089:          ELOG( "Error : ID3D12Resource::Map() Failed." );
00090:          return false;
00091:      }
00092:  
00093:      return true;
00094:  }
続いて,先ほど構築したテクスチャリストを使ってシェーダに送るためにシェーダリソースビューを作っていきます。
00788:      // テクスチャ・シェーダリソースビューの生成.
00789:      {
00790:          m_ModelTexture.resize( textureNameList.size() );
00791:  
00792:          auto idx = 0;
00793:          auto offset = 1 + static_cast<u32>(m_ModelData.Materials.size());
00794:  
00795:          for( auto textureName : textureNameList )
00796:          {
00797:              auto ext = asdx::GetExtW( textureName.c_str() );
00798:  
00799:              // DDSを読み込みリソースを生成.
00800:              if ( ext == L"dds" )
00801:              {
00802:                  asdx::ResDDS dds;
00803:                  if ( dds.Load( textureName.c_str() ) )
00804:                  {
00805:                      auto desc = asdx::Texture::Desc();
00806:                      desc.ResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
00807:                      desc.ResourceDesc.Width     = dds.GetWidth();
00808:                      desc.ResourceDesc.Height    = dds.GetHeight();
00809:                      desc.ResourceDesc.MipLevels = 1;
00810:                      desc.ResourceDesc.Format    = static_cast<DXGI_FORMAT>(dds.GetFormat());
00811:                      desc.HandleCPU              = m_Heap[DESC_HEAP_BUFFER].GetHandleCPU( offset + idx );
00812:  
00813:                      if ( !m_ModelTexture[idx].Init( m_pDevice.GetPtr(), desc ) )
00814:                      {
00815:                          continue;
00816:                      }
00817:  
00818:                      // サブリソースを設定.
00819:                      D3D12_SUBRESOURCE_DATA res = {};
00820:                      res.pData      = dds.GetSurfaces()->pPixels;
00821:                      res.RowPitch   = dds.GetSurfaces()->Pitch;
00822:                      res.SlicePitch = dds.GetSurfaces()->SlicePitch;
00823:  
00824:                      asdx::RefPtr<ID3D12Resource> intermediate;
00825:  
00826:                      m_Immediate.Clear( m_pPipelineState.GetPtr() );
00827:  
00828:                      // サブリソースを更新.
00829:                      if ( !m_ModelTexture[idx].Upload(
00830:                          m_Immediate.GetGfxCmdList(),
00831:                          &res,
00832:                          intermediate.GetAddress() ) )
00833:                      {
00834:                          continue;
00835:                      }
00836:  
00837:                      // コマンドリストを閉じておく.
00838:                      m_Immediate->Close();
00839:  
00840:                      // GPUにテクスチャ転送.
00841:                      m_Immediate.Execute( m_pCmdQueue.GetPtr() );
00842:  
00843:                      // 実行完了を待機.
00844:                      WaitForGpu();
00845:  
00846:                  }
00847:              }
00848:  
00849:              idx++;
00850:          }
00851:      }
テクスチャの初期化処理やサブリソースの更新もちょっと面倒なので,クラス化してあります。
テクスチャの初期化処理は下記のようになります。
00268:  //-------------------------------------------------------------------------------------------------
00269:  //      初期化処理です.
00270:  //-------------------------------------------------------------------------------------------------
00271:  bool Texture::Init( ID3D12Device* pDevice, Desc& desc )
00272:  {
00273:      if ( pDevice == nullptr )
00274:      {
00275:          ELOG( "Error : Invalid Argument." );
00276:          return false;
00277:      }
00278:  
00279:      if ( desc.ResourceDesc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE1D &&
00280:           desc.ResourceDesc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D &&
00281:           desc.ResourceDesc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE3D)
00282:      {
00283:          ELOG( "Error : Invalid Resource Dimension." );
00284:          return false;
00285:      }
00286:  
00287:      HRESULT hr = S_OK;
00288:  
00289:      D3D12_HEAP_PROPERTIES props = {
00290:          D3D12_HEAP_TYPE_DEFAULT,
00291:          D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
00292:          D3D12_MEMORY_POOL_UNKNOWN,
00293:          1,
00294:          1
00295:      };
00296:  
00297:      m_State = D3D12_RESOURCE_STATE_COMMON;
00298:  
00299:      hr = pDevice->CreateCommittedResource(
00300:          &props,
00301:          D3D12_HEAP_FLAG_NONE,
00302:          &desc.ResourceDesc,
00303:          D3D12_RESOURCE_STATE_COMMON,
00304:          nullptr,
00305:          IID_PPV_ARGS( m_Resource.GetAddress() ) );
00306:      if ( FAILED( hr ) )
00307:      {
00308:          ELOG( "Error : ID3D12Device::CreateCommittedResource() Failed." );
00309:          return false;
00310:      }
00311:  
00312:      auto mipLevel = ( desc.ResourceDesc.MipLevels == 0 ) ? -1 : desc.ResourceDesc.MipLevels;
00313:  
00314:      D3D12_SHADER_RESOURCE_VIEW_DESC viewDesc = {};
00315:      viewDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
00316:      viewDesc.Format = desc.ResourceDesc.Format;
00317:  
00318:      switch( desc.ResourceDesc.Dimension )
00319:      {
00320:      case D3D12_RESOURCE_DIMENSION_TEXTURE1D:
00321:          {
00322:              if ( desc.ResourceDesc.DepthOrArraySize > 1 )
00323:              {
00324:                  viewDesc.ViewDimension                  = D3D12_SRV_DIMENSION_TEXTURE1DARRAY;
00325:                  viewDesc.Texture1DArray.ArraySize       = desc.ResourceDesc.DepthOrArraySize;
00326:                  viewDesc.Texture1DArray.FirstArraySlice = 0;
00327:                  viewDesc.Texture1DArray.MipLevels       = mipLevel;
00328:              }
00329:              else
00330:              {
00331:                  viewDesc.ViewDimension       = D3D12_SRV_DIMENSION_TEXTURE1D;
00332:                  viewDesc.Texture1D.MipLevels = mipLevel;
00333:              }
00334:          }
00335:          break;
00336:  
00337:      case D3D12_RESOURCE_DIMENSION_TEXTURE2D:
00338:          {
00339:              if ( desc.IsCubeMap )
00340:              {
00341:                  if ( desc.ResourceDesc.DepthOrArraySize > 6 )
00342:                  {
00343:                      viewDesc.ViewDimension              = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY;
00344:                      viewDesc.TextureCubeArray.MipLevels = mipLevel;
00345:                      viewDesc.TextureCubeArray.NumCubes  = ( desc.ResourceDesc.DepthOrArraySize / 6 );
00346:                  }
00347:                  else
00348:                  {
00349:                      viewDesc.ViewDimension         = D3D12_SRV_DIMENSION_TEXTURECUBE;
00350:                      viewDesc.TextureCube.MipLevels = mipLevel;
00351:                  }
00352:              }
00353:              else if ( desc.ResourceDesc.SampleDesc.Count > 1 )
00354:              {
00355:                  if ( desc.ResourceDesc.DepthOrArraySize > 1 )
00356:                  {
00357:                      viewDesc.ViewDimension                    = D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY;
00358:                      viewDesc.Texture2DMSArray.ArraySize       = desc.ResourceDesc.DepthOrArraySize;
00359:                      viewDesc.Texture2DMSArray.FirstArraySlice = 0;
00360:                  }
00361:                  else
00362:                  {
00363:                      viewDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMS;
00364:                  }
00365:              }
00366:              else
00367:              {
00368:                  if ( desc.ResourceDesc.DepthOrArraySize > 1 )
00369:                  {
00370:                      viewDesc.ViewDimension            = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
00371:                      viewDesc.Texture2DArray.ArraySize = desc.ResourceDesc.DepthOrArraySize;
00372:                      viewDesc.Texture2DArray.MipLevels = mipLevel;
00373:                  }
00374:                  else
00375:                  {
00376:                      viewDesc.ViewDimension             = D3D12_SRV_DIMENSION_TEXTURE2D;
00377:                      viewDesc.Texture2D.MipLevels       = mipLevel;
00378:                      viewDesc.Texture2D.MostDetailedMip = 0;
00379:                  }
00380:              }
00381:          }
00382:          break;
00383:  
00384:      case D3D12_RESOURCE_DIMENSION_TEXTURE3D:
00385:          {
00386:              viewDesc.ViewDimension             = D3D12_SRV_DIMENSION_TEXTURE3D;
00387:              viewDesc.Texture3D.MipLevels       = mipLevel;
00388:              viewDesc.Texture3D.MostDetailedMip = 0;
00389:          }
00390:          break;
00391:      }
00392:  
00393:      pDevice->CreateShaderResourceView( m_Resource.GetPtr(), &viewDesc, desc.HandleCPU );
00394:  
00395:      return true;
00396:  }
サブリソースの更新処理は下記のような感じです。
00406:  //-------------------------------------------------------------------------------------------------
00407:  //      サブリソースを更新します.
00408:  //-------------------------------------------------------------------------------------------------
00409:  bool Texture::Upload
00410:  (
00411:      ID3D12GraphicsCommandList*  pCmdList,
00412:      D3D12_SUBRESOURCE_DATA*     pData,
00413:      ID3D12Resource**            ppIntermediate
00414:  )
00415:  {
00416:      if ( pCmdList == nullptr || pData == nullptr )
00417:      {
00418:          ELOG( "Error : Invalid Argument." );
00419:          return false;
00420:      }
00421:  
00422:      RefPtr<ID3D12Device> device;
00423:      m_Resource->GetDevice( IID_PPV_ARGS(device.GetAddress()));
00424:  
00425:      {
00426:          // アップロード用リソースを生成.
00427:  
00428:          D3D12_RESOURCE_DESC uploadDesc = {
00429:              D3D12_RESOURCE_DIMENSION_BUFFER,
00430:              0,
00431:              GetRequiredIntermediateSize( m_Resource.GetPtr(), 0, 1 ),
00432:              1,
00433:              1,
00434:              1,
00435:              DXGI_FORMAT_UNKNOWN,
00436:              { 1, 0 },
00437:              D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
00438:              D3D12_RESOURCE_FLAG_NONE
00439:          };
00440:  
00441:          D3D12_HEAP_PROPERTIES props = {
00442:              D3D12_HEAP_TYPE_UPLOAD,
00443:              D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
00444:              D3D12_MEMORY_POOL_UNKNOWN,
00445:              1,
00446:              1
00447:          };
00448:  
00449:          auto hr = device->CreateCommittedResource(
00450:              &props,
00451:              D3D12_HEAP_FLAG_NONE,
00452:              &uploadDesc,
00453:              D3D12_RESOURCE_STATE_GENERIC_READ,
00454:              nullptr,
00455:              IID_PPV_ARGS(ppIntermediate));
00456:          if ( FAILED( hr ) )
00457:          {
00458:              ELOG( "Error : ID3D12Device::CreateCommittedResource() Failed." );
00459:              return false;
00460:          }
00461:      }
00462:  
00463:      D3D12_RESOURCE_STATES oldState = m_State;
00464:      m_State = D3D12_RESOURCE_STATE_COPY_DEST;
00465:  
00466:      D3D12_RESOURCE_BARRIER barrier = {};
00467:      barrier.Type                   = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
00468:      barrier.Transition.pResource   = m_Resource.GetPtr();
00469:      barrier.Transition.StateBefore = oldState;
00470:      barrier.Transition.StateAfter  = m_State;
00471:      barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
00472:      barrier.Flags                  = D3D12_RESOURCE_BARRIER_FLAG_NONE;
00473:      pCmdList->ResourceBarrier( 1, &barrier );
00474:  
00475:      UpdateSubresources(
00476:          pCmdList,
00477:          m_Resource.GetPtr(),
00478:          (*ppIntermediate),
00479:          0,
00480:          0,
00481:          1,
00482:          pData );
00483:  
00484:      oldState = m_State;
00485:      m_State = D3D12_RESOURCE_STATE_GENERIC_READ;
00486:  
00487:      barrier.Transition.StateBefore = oldState;
00488:      barrier.Transition.StateAfter  = m_State;
00489:      pCmdList->ResourceBarrier( 1, &barrier );
00490:  
00491:      return true;
00492:  }

00042:  //-------------------------------------------------------------------------------------------------
00043:  //      データのアップロードに必要なサイズを取得します.
00044:  //-------------------------------------------------------------------------------------------------
00045:  u64 GetRequiredIntermediateSize
00046:  (
00047:      ID3D12Resource* pDstResource,
00048:      u32             firstSubresource,
00049:      u32             subResourceCount
00050:  )
00051:  {
00052:      auto desc = pDstResource->GetDesc();
00053:      u64 result = 0;
00054:      
00055:      ID3D12Device* pDevice;
00056:      pDstResource->GetDevice( IID_PPV_ARGS( &pDevice ) );
00057:      assert( pDevice != nullptr );
00058:      pDevice->GetCopyableFootprints(
00059:          &desc, 
00060:          firstSubresource,
00061:          subResourceCount,
00062:          0,
00063:          nullptr,
00064:          nullptr,
00065:          nullptr,
00066:          &result );
00067:      pDevice->Release();
00068:      
00069:      return result;
00070:  }

00159:  //-------------------------------------------------------------------------------------------------
00160:  //      サブリソースを更新します.
00161:  //-------------------------------------------------------------------------------------------------
00162:  u64 UpdateSubresources
00163:  ( 
00164:      ID3D12GraphicsCommandList*  pCmdList,
00165:      ID3D12Resource*             pDstResource,
00166:      ID3D12Resource*             pIntermediate,
00167:      u64                         intermediateOffset,
00168:      u32                         firstSubresource,
00169:      u32                         subResourceCount,
00170:      D3D12_SUBRESOURCE_DATA*     pSrcData
00171:  )
00172:  {
00173:      u64 requiredSize = 0;
00174:      auto bufferSize = static_cast<u64>(
00175:          sizeof(D3D12_PLACED_SUBRESOURCE_FOOTPRINT) +
00176:          sizeof(u32) +
00177:          sizeof(u64) ) * subResourceCount;
00178:      if ( bufferSize > SIZE_MAX )
00179:      { return 0; }
00180:  
00181:      auto buffer = HeapAlloc(GetProcessHeap(), 0, static_cast<size_t>(bufferSize));
00182:      if (buffer == nullptr)
00183:      { return 0; }
00184:  
00185:      auto pLayouts         = reinterpret_cast<D3D12_PLACED_SUBRESOURCE_FOOTPRINT*>(buffer);
00186:      auto pRowSizesInBytes = reinterpret_cast<u64*>(pLayouts + subResourceCount);
00187:      auto pRowCounts       = reinterpret_cast<u32*>(pRowSizesInBytes + subResourceCount);
00188:      
00189:      auto desc = pDstResource->GetDesc();
00190:      ID3D12Device* pDevice;
00191:      pDstResource->GetDevice( IID_PPV_ARGS( &pDevice ) );
00192:      pDevice->GetCopyableFootprints(
00193:          &desc,
00194:          firstSubresource,
00195:          subResourceCount,
00196:          intermediateOffset,
00197:          pLayouts,
00198:          pRowCounts,
00199:          pRowSizesInBytes,
00200:          &requiredSize);
00201:      pDevice->Release();
00202:      
00203:      auto result = UpdateSubresources(
00204:          pCmdList,
00205:          pDstResource,
00206:          pIntermediate,
00207:          firstSubresource,
00208:          subResourceCount,
00209:          requiredSize,
00210:          pLayouts,
00211:          pRowCounts,
00212:          pRowSizesInBytes,
00213:          pSrcData);
00214:  
00215:      HeapFree(GetProcessHeap(), 0, buffer);
00216:  
00217:      return result;
00218:  }

00072:  //-------------------------------------------------------------------------------------------------
00073:  //      サブリソースを更新します.
00074:  //-------------------------------------------------------------------------------------------------
00075:  inline u64 UpdateSubresources
00076:  (
00077:      ID3D12GraphicsCommandList* pCmdList,
00078:      ID3D12Resource* pDstResource,
00079:      ID3D12Resource* pIntermediate,
00080:      u32 firstSubresource,
00081:      u32 subResourceCount,
00082:      u64 requiredSize,
00083:      const D3D12_PLACED_SUBRESOURCE_FOOTPRINT* pLayouts,
00084:      const u32* pRowCounts,
00085:      const u64* pRowSizesInBytes,
00086:      const D3D12_SUBRESOURCE_DATA* pSrcData
00087:  )
00088:  {
00089:      auto imdDesc = pIntermediate->GetDesc();
00090:      auto dstDesc = pDstResource->GetDesc();
00091:      if ( imdDesc.Dimension != D3D12_RESOURCE_DIMENSION_BUFFER || 
00092:           imdDesc.Width < requiredSize + pLayouts[0].Offset || 
00093:           requiredSize > size_t(-1) || 
00094:           (dstDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER && (firstSubresource != 0 || subResourceCount != 1)) )
00095:      { return 0; }
00096:      
00097:      u8* pData;
00098:      auto hr = pIntermediate->Map(0, nullptr, reinterpret_cast<void**>(&pData));
00099:      if ( FAILED( hr ) )
00100:      { return 0; }
00101:      
00102:      for ( u32 i=0; i <subResourceCount; ++i )
00103:      {
00104:          if ( pRowSizesInBytes[i] > size_t(-1) )
00105:          { return 0; }
00106:  
00107:          D3D12_MEMCPY_DEST dstData = { 
00108:              pData + pLayouts[i].Offset, 
00109:              pLayouts[i].Footprint.RowPitch,
00110:              pLayouts[i].Footprint.RowPitch * pRowCounts[i]
00111:          };
00112:          CopySubresource(
00113:              &dstData,
00114:              &pSrcData[i],
00115:              size_t(pRowSizesInBytes[i]),
00116:              pRowCounts[i],
00117:              pLayouts[i].Footprint.Depth );
00118:      }
00119:      pIntermediate->Unmap(0, nullptr);
00120:      
00121:      if ( dstDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER )
00122:      {
00123:          D3D12_BOX srcBox;
00124:          srcBox.left   = u32( pLayouts[0].Offset );
00125:          srcBox.right  = u32( pLayouts[0].Offset + pLayouts[0].Footprint.Width );
00126:          srcBox.top    = 0;
00127:          srcBox.front  = 0;
00128:          srcBox.bottom = 1;
00129:          srcBox.back   = 1;
00130:          pCmdList->CopyBufferRegion(
00131:              pDstResource,
00132:              0,
00133:              pIntermediate,
00134:              pLayouts[0].Offset,
00135:              pLayouts[0].Footprint.Width );
00136:      }
00137:      else
00138:      {
00139:          for ( u32 i=0; i<subResourceCount; ++i )
00140:          {
00141:              D3D12_TEXTURE_COPY_LOCATION dst;
00142:              D3D12_TEXTURE_COPY_LOCATION src;
00143:  
00144:              dst.pResource        = pDstResource;
00145:              dst.Type             = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
00146:              dst.SubresourceIndex = i + firstSubresource;
00147:  
00148:              src.pResource       = pIntermediate;
00149:              src.Type            = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
00150:              src.PlacedFootprint = pLayouts[i];
00151:  
00152:              pCmdList->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr);
00153:          }
00154:      }
00155:  
00156:      return requiredSize;
00157:  }
ちょっとUpload()メソッドの仕様がやっつけ感がありますが,とりあえず今回のサンプルは1回こっきりで使いまわさないことを前提にして作っているので,このままにしておきます。
これでようやくコマンドを作成するための下準備が整いました。モデルの描画は結構負荷が高いのでコマンドを全てBundleに積んでしまいます。前回やったBundleがこういうところで威力を発揮します。
00853:      // バンドルの初期化.
00854:      if ( !m_Bundle.Init( m_pDevice.GetPtr(), D3D12_COMMAND_LIST_TYPE_BUNDLE, m_pPipelineState.GetPtr() ) )
00855:      {
00856:          ELOG( "Error : GraphicsCmdList()::Init() Failed." );
00857:          return false;
00858:      }
00859:  
00860:      // バンドルにコマンドを積む.
00861:      {
00862:          // ディスクリプタヒープを設定.
00863:          ID3D12DescriptorHeap* pHeap = m_Heap[DESC_HEAP_BUFFER].GetPtr();
00864:          m_Bundle->SetDescriptorHeaps( 1, &pHeap );
00865:  
00866:          // ルートシグニチャを設定.
00867:          m_Bundle->SetGraphicsRootSignature( m_pRootSignature.GetPtr() );
00868:  
00869:          // プリミティブトポロジーの設定.
00870:          m_Bundle->IASetPrimitiveTopology( D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
00871:  
00872:          auto vbv = m_ModelVB.GetView();
00873:          auto ibv = m_ModelIB.GetView();
00874:  
00875:          // 頂点バッファビューを設定.
00876:          m_Bundle->IASetVertexBuffers( 0, 1, &vbv );
00877:          m_Bundle->IASetIndexBuffer( &ibv );
00878:  
00879:          // インデックスオフセット.
00880:          u32 offset = 0;
00881:          u32 materialCount = u32( m_ModelData.Materials.size() );
00882:  
00883:          for( size_t i=0; i<materialCount; ++i )
00884:          {
00885:              // テクスチャとマテリアルを設定.
00886:              auto handleSRV = m_Heap[DESC_HEAP_BUFFER].GetHandleGPU( 1 + materialCount + textureIdList[i] );
00887:              auto handleMat = m_Heap[DESC_HEAP_BUFFER].GetHandleGPU( 1 + u32(i) );
00888:              m_Bundle->SetGraphicsRootDescriptorTable( 1, handleSRV );
00889:              m_Bundle->SetGraphicsRootDescriptorTable( 2, handleMat );
00890:  
00891:              u32 count = m_ModelData.Materials[i].VertexCount;
00892:  
00893:              // 描画コマンドを生成.
00894:              m_Bundle->DrawIndexedInstanced( count, 1, offset, 0, 0 );
00895:  
00896:              offset += count;
00897:          }
00898:  
00899:          // バンドルへの記録を終了.
00900:          m_Bundle->Close();
00901:      }
ちなみ,ここまでは全て初期化処理です。
あとは,フレームを描画する際にBundleを実行してやればよいので,フレーム描画の処理は前回とほとんど変わりません。
00932:  //-------------------------------------------------------------------------------------------------
00933:  //      描画時の処理です.
00934:  //-------------------------------------------------------------------------------------------------
00935:  void App::OnRender(FLOAT elapsedSec)
00936:  {
00937:      // 回転角を増やす.
00938:      m_RotateAngle += ( elapsedSec / 1.5f );
00939:  
00940:      // ワールド行列を更新.
00941:      m_ModelParam.World = asdx::Matrix::CreateRotationY( m_RotateAngle );
00942:  
00943:      // 定数バッファを更新.
00944:      m_ModelTB.Update( &m_ModelParam, sizeof(m_ModelParam), 0 );
00945:  
00946:      // コマンドアロケータとコマンドリストをリセット.
00947:      m_Immediate.Clear( m_pPipelineState.GetPtr() );
00948:  
00949:      ID3D12DescriptorHeap* pHeap = m_Heap[DESC_HEAP_BUFFER].GetPtr();
00950:  
00951:      // ディスクリプタヒープを設定.
00952:      m_Immediate->SetDescriptorHeaps( 1, &pHeap );
00953:  
00954:      // ルートシグニチャを設定.
00955:      m_Immediate->SetGraphicsRootSignature( m_pRootSignature.GetPtr() );
00956:  
00957:      // ディスクリプタヒープテーブルを設定.
00958:      auto handleCBV = m_Heap[DESC_HEAP_BUFFER].GetHandleGPU(0);
00959:      m_Immediate->SetGraphicsRootDescriptorTable( 0, handleCBV );
00960:  
00961:      // リソースバリアの設定.
00962:      m_Immediate.Transition(
00963:          m_pRenderTarget[m_FrameIndex].GetPtr(),
00964:          D3D12_RESOURCE_STATE_PRESENT,
00965:          D3D12_RESOURCE_STATE_RENDER_TARGET );
00966:  
00967:      // ビューポートの設定.
00968:      m_Immediate->RSSetViewports( 1, &m_Viewport );
00969:  
00970:      // シザー矩形の設定.
00971:      m_Immediate->RSSetScissorRects( 1, &m_ScissorRect );
00972:  
00973:      // レンダーターゲットのハンドルを取得.
00974:      auto handleRTV = m_Heap[DESC_HEAP_RTV].GetHandleCPU(m_FrameIndex);
00975:      auto handleDSV = m_Heap[DESC_HEAP_DSV].GetHandleCPU(0);
00976:  
00977:      // レンダーターゲットの設定.
00978:      m_Immediate->OMSetRenderTargets( 1, &handleRTV, FALSE, &handleDSV );
00979:  
00980:      // レンダーターゲットビューをクリア.
00981:      const FLOAT clearColor[] = { 0.39f, 0.58f, 0.92f, 0.0f };
00982:      m_Immediate->ClearRenderTargetView( handleRTV, clearColor, 0, nullptr );
00983:  
00984:      // 深度ステンシルビューをクリア.
00985:      m_Immediate->ClearDepthStencilView( handleDSV, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr );
00986:  
00987:      // バンドルを実行.
00988:      m_Immediate->ExecuteBundle( m_Bundle.GetGfxCmdList() );
00989:  
00990:      // リソースバリアの設定.
00991:      m_Immediate.Transition(
00992:          m_pRenderTarget[m_FrameIndex].GetPtr(),
00993:          D3D12_RESOURCE_STATE_RENDER_TARGET,
00994:          D3D12_RESOURCE_STATE_PRESENT );
00995:  
00996:      // コマンドの記録を終了.
00997:      m_Immediate->Close();
00998:  
00999:      // コマンド実行.
01000:      m_Immediate.Execute( m_pCmdQueue.GetPtr() );
01001:  
01002:      // 表示する.
01003:      m_pSwapChain->Present( 1, 0 );
01004:  
01005:      // コマンドの完了を待機.
01006:      WaitForGpu();
01007:  }
初期化処理はかなりごちゃごちゃしますが,フレーム描画処理はスッキリしてよいですね。


 4.おわりに
かなり長くなってしまいましたが,一応モデル表示について説明してみました。
頂点データ等を作るのはさほど手間ではないのですが,やっぱりテクスチャデータの取り扱いがDirect3D12は面倒くさすぎて,全くやる気が出ないですね。
次回はモーションデータを取り扱おうかなって思っています。


 Download
本ソースコードおよびプログラムはMIT Licenseに準じます。
プログラムの作成にはMicrosoft Visual Studio Community 2015, 及び Windows SDK 10.0.10240.0を用いています。