DirectX12でミクさんを躍らせてみよう5－PMX ファイル読み込み

# 前回
https://qiita.com/dpals39/items/c99a229c39f2a14256a0

# PMX ファイル読み込み
PMXモデルファイルをロードして、ミクさんを描画できるようにしましょう。

PMXフォーマットは元々MMD（MikuMikuDance）で使用されるフォーマットです。

以前はPMDを使用していましたが、PMDファイルフォーマットから改良されたファイル形式がPMXです。
使用するPMXファイルは自分で探して使用してください。

# PMXファイルの構造
PMXファイルは以下のようなデータ構造を持っています。
- ヘッダー情報
- モデル情報（名前、コメント）
- 頂点情報
- フェイス情報（インデックスと考えてください）
- テクスチャ情報
- マテリアル情報
- ボーン情報
- モーフ情報
- ディスプレイフレーム情報（使用しません）
- リジッドボディ情報
- ジョイント情報
- 剛体情報

上記に列挙した順序で情報が並んでいます。

# PMXファイルの読み込み
PMXフォーマットはバイナリデータです。
C++のifstreamを使用してPMXファイルを開き、readを通じて必要な情報をバイト単位で読み込むようにします。
```cpp
	std::ifstream pmxFile{ filePath, (std::ios::binary | std::ios::in) };
	if (pmxFile.fail())
	{
		pmxFile.close();
		return false;
	}
```
filePathはpmxファイルのパスとファイル名の文字列です。ifstreamを使用してバイナリモード、読み取り専用モードでファイルを開きます。

PMXFileData 構造体を次のように定義しました。
```cpp
struct PMXFileData
{
	PMXHeader header;
	PMXModelInfo modelInfo;

	std::vector<PMXVertex> vertices;
	std::vector<PMXFace> faces;
	std::vector<PMXTexture> textures;
	std::vector<PMXMaterial> materials;
	std::vector<PMXBone> bones;
	std::vector<PMXMorph> morphs;
	std::vector<PMXDisplayFrame> displayFrames;
	std::vector<PMXRigidBody> rigidBodies;
	std::vector<PMXJoint> joints;
	std::vector<PMXSoftBody> softBodies;
};
```

# ヘッダーの読み込み
PMXファイルのヘッダー情報に関する構造体を次のように定義しました。
```cpp
struct PMXHeader
{
	std::array<unsigned char, 4> magic;
	float version;
	unsigned char dataLength;
	unsigned char textEncoding;      // 0 = utf-16, 1= utf - 8
	unsigned char addUVNum;          // 0 - 4
	unsigned char vertexIndexSize;   // 1 = byte, 2 = short, 4 = int
	unsigned char textureIndexSize;  // 1 = byte, 2 = short, 4 = int
	unsigned char materialIndexSize;
	unsigned char boneIndexSize;
	unsigned char morphIndexSize;
	unsigned char rigidBodyIndexSize;
};
```
ヘッダー情報は各パラメーターの型のサイズだけ読み込めば良いです。

ヘッダー情報を読み込むメソッドを私はこのように作成しました。
```cpp
bool ReadHeader(PMXFileData& data, std::ifstream& file)
```

最初の4バイトはPMXファイルのマジックナンバーです。PMXファイルであれば、必ず決まった値が最初の4バイトに含まれています。
```cpp
constexpr std::array<unsigned char, 4> PMX_MAGIC_NUMBER{ 0x50, 0x4d, 0x58, 0x20 };
```
この値と最初の4バイトの値を比較したとき、一致しない場合はPMXファイル形式ではないため、ファイルの読み込みを中止します。
```cpp
file.read(reinterpret_cast<char*>(data.header.magic.data()), data.header.magic.size());
	if (data.header.magic != PMX_MAGIC_NUMBER)
	{
		return false;
	}
```

```cpp
file.read(reinterpret_cast<char*>(&data.header.version), sizeof(data.header.version));
file.read(reinterpret_cast<char*>(&data.header.dataLength), sizeof(data.header.dataLength));
file.read(reinterpret_cast<char*>(&data.header.textEncoding), sizeof(data.header.textEncoding));
```
次の4バイトはファイルのバージョン
次の1バイトはデータのサイズ
次の1バイトはテキストエンコーディング

これらのデータは私たちにとって重要な情報ではありません。

```
file.read(reinterpret_cast<char*>(&data.header.addUVNum), sizeof(data.header.addUVNum));
```
次の1バイトは追加UVの数です。基本のUV値以外に追加で使用するUVの数です。
後で頂点情報を読み込む際に使用されます。

```cpp
file.read(reinterpret_cast<char*>(&data.header.vertexIndexSize), sizeof(data.header.vertexIndexSize));
file.read(reinterpret_cast<char*>(&data.header.textureIndexSize), sizeof(data.header.textureIndexSize));
file.read(reinterpret_cast<char*>(&data.header.materialIndexSize), sizeof(data.header.materialIndexSize));
file.read(reinterpret_cast<char*>(&data.header.boneIndexSize), sizeof(data.header.boneIndexSize));
file.read(reinterpret_cast<char*>(&data.header.morphIndexSize), sizeof(data.header.morphIndexSize));
file.read(reinterpret_cast<char*>(&data.header.rigidBodyIndexSize), sizeof(data.header.rigidBodyIndexSize));
```
その後も順番に1バイトずつ読み込んでいきますが、これらはすべて後で読み込む情報のバイト数です。

# モデル情報の読み込み
モデル情報を読み込みましょう。
モデル情報はモデルの名前とコメントの文字列です。
```cpp
bool ReadModelInfo(PMXFileData& data, std::ifstream& file)
{
	UnicodeUtil::GetPMXStringUTF16(file, data.modelInfo.modelName);
	UnicodeUtil::GetPMXStringUTF8(file, data.modelInfo.englishModelName);
	UnicodeUtil::GetPMXStringUTF16(file, data.modelInfo.comment);
	UnicodeUtil::GetPMXStringUTF8(file, data.modelInfo.englishComment);

	return true;
}
```
PMXファイルは名前とコメントのUTF16文字列とUTF8文字列を持っているため、それぞれ別々に読み込む必要があります。

UTF16の文字列をstd::wstringに変換するメソッドを作成する必要があります。
```cpp
bool GetPMXStringUTF16(std::ifstream& _file, std::wstring& output)
{
	std::array<wchar_t, 512> wBuffer{};
	int textSize;

	_file.read(reinterpret_cast<char*>(&textSize), 4);

	_file.read(reinterpret_cast<char*>(&wBuffer), textSize);
	output = std::wstring(&wBuffer[0], &wBuffer[0] + textSize / 2);

	return true;
}
```
最初の4バイトは、その文字列のバイト数です。
文字列のバイト数を読み込んだ後、その分だけ読み込んでwbufferに保存しましょう。
wstring コンストラクタに開始と終了のポインタを渡して、読み込んだバイトをwstringに変換します。
texSizeを2で割る理由は、UTF16の文字が2バイトだからです。
例えば、texSizeが16の場合、文字数は8個ということになります。

UTF8の文字列はstd::stringに保存します。
```cpp
bool GetPMXStringUTF8(std::ifstream& _file, std::string& output)
{
    std::array<wchar_t, 512> wBuffer{};
	int textSize;

	_file.read(reinterpret_cast<char*>(&textSize), 4);
	_file.read(reinterpret_cast<char*>(&wBuffer), textSize);

	output = std::string(&wBuffer[0], &wBuffer[0] + textSize);

	return true;
}
```

# 頂点情報の読み込み
今回は頂点情報を読み込みましょう。
PMXの頂点構造体は次のように構成されています。
```cpp
struct PMXVertex
{
	DirectX::XMFLOAT3 position;        
	DirectX::XMFLOAT3 normal;          
	DirectX::XMFLOAT2 uv;              
	DirectX::XMFLOAT4 additionalUV[4]; 

	PMXVertexWeight weightType;
	int boneIndices[4];
	float boneWeights[4];
	DirectX::XMFLOAT3 sdefC;
	DirectX::XMFLOAT3 sdefR0;
	DirectX::XMFLOAT3 sdefR1;

	float edgeMag;
};
```

頂点情報を読み込むメソッドは次のとおりです。
```cpp
bool ReadVertex(PMXFileData& data, std::ifstream& file)
```
最初の4バイトはこのモデルの頂点数です。
```cpp
unsigned int vertexCount;
file.read(reinterpret_cast<char*>(&vertexCount), 4);
data.vertices.resize(vertexCount);
```
この頂点数で頂点Vectorをリサイズし、forループで頂点を読み込んでいきます。
```cpp
for (auto& vertex : data.vertices)
{
}
```
最初の32バイトは位置、法線、UV値です。
```cpp
file.read(reinterpret_cast<char*>(&vertex.position), 12);
file.read(reinterpret_cast<char*>(&vertex.normal), 12);
file.read(reinterpret_cast<char*>(&vertex.uv), 8);
```
次は追加UVですが、先ほどヘッダーを読み込む際にこの追加UVの数を読み込みました。
この数だけ16バイトずつ読み込みます。
```cpp
for (int i = 0; i < data.header.addUVNum; i++)
{
	file.read(reinterpret_cast<char*>(&vertex.additionalUV[i]), 16);
}
```

次にボーンのウェイトタイプと現在の頂点にリギングされたボーンのインデックスを読み込みます。
```cpp
file.read(reinterpret_cast<char*>(&vertex.weightType), 1);

	const unsigned char boneIndexSize = data.header.boneIndexSize;

	switch (vertex.weightType)
	{
		case PMXVertexWeight::BDEF1:
			file.read(reinterpret_cast<char*>(&vertex.boneIndices[0]), boneIndexSize);
			break;
		case PMXVertexWeight::BDEF2:
			file.read(reinterpret_cast<char*>(&vertex.boneIndices[0]), boneIndexSize);
			file.read(reinterpret_cast<char*>(&vertex.boneIndices[1]), boneIndexSize);
			file.read(reinterpret_cast<char*>(&vertex.boneWeights[0]), 4);
			break;
		case PMXVertexWeight::BDEF4:
		case PMXVertexWeight::QDEF:
			file.read(reinterpret_cast<char*>(&vertex.boneIndices[0]), boneIndexSize);
			file.read(reinterpret_cast<char*>(&vertex.boneIndices[1]), boneIndexSize);
			file.read(reinterpret_cast<char*>(&vertex.boneIndices[2]), boneIndexSize);
			file.read(reinterpret_cast<char*>(&vertex.boneIndices[3]), boneIndexSize);
			file.read(reinterpret_cast<char*>(&vertex.boneWeights[0]), 4);
			file.read(reinterpret_cast<char*>(&vertex.boneWeights[1]), 4);
			file.read(reinterpret_cast<char*>(&vertex.boneWeights[2]), 4);
			file.read(reinterpret_cast<char*>(&vertex.boneWeights[3]), 4);
			break;
		case PMXVertexWeight::SDEF:
			file.read(reinterpret_cast<char*>(&vertex.boneIndices[0]), boneIndexSize);
			file.read(reinterpret_cast<char*>(&vertex.boneIndices[1]), boneIndexSize);
			file.read(reinterpret_cast<char*>(&vertex.boneWeights[0]), 4);
			file.read(reinterpret_cast<char*>(&vertex.sdefC), 12);
			file.read(reinterpret_cast<char*>(&vertex.sdefR0), 12);
			file.read(reinterpret_cast<char*>(&vertex.sdefR1), 12);
			break;
		default:
			return false;
	}
```
ここでもヘッダーにあったboneIndexSizeの分だけリギングされたボーンのインデックス値を読み込みます。

スキニングアニメーションについて知らない方であれば、リギング、ウェイト、ボーンが何なのか分からないでしょうが、モデルのアニメーションのためのデータだと考えてください。
スキニングアニメーションについては後ほど扱うことにします。

最後にedgeMagを読み込むと、1つの頂点情報を全て読み込んだことになります。
forループを閉じてください。
```cpp
  file.read(reinterpret_cast<char*>(&vertex.edgeMag), 4);
}
```

# フェイス情報の読み込み
フェイスは三角形1つのインデックスリストだと考えてください。
```cpp
struct PMXFace
{
	int vertices[3];
};
```

読み込むメソッドは次のとおりです。
```cpp
bool ReadFace(PMXFileData& data, std::ifstream& file)
```

```cpp
int faceCount = 0;
file.read(reinterpret_cast<char*>(&faceCount), 4);

faceCount /= 3;
data.faces.resize(faceCount);
```
最初の4バイトはインデックスの数です。
インデックス3つで1つのフェイスを構成するので、3で割るとフェイスの数になります。

```cpp
switch (data.header.vertexIndexSize)
{
	case 1:
	{
		std::vector<uint8_t> vertices(faceCount * 3);
		file.read(reinterpret_cast<char*>(vertices.data()), sizeof(uint8_t) * vertices.size());
		for (int32_t faceIdx = 0; faceIdx < faceCount; faceIdx++)
		{
			data.faces[faceIdx].vertices[0] = vertices[faceIdx * 3 + 0];
			data.faces[faceIdx].vertices[1] = vertices[faceIdx * 3 + 1];
			data.faces[faceIdx].vertices[2] = vertices[faceIdx * 3 + 2];
		}
	}
		break;
	case 2:
	{
		std::vector<uint16_t> vertices(faceCount * 3);
		file.read(reinterpret_cast<char*>(vertices.data()), sizeof(uint16_t) * vertices.size());
		for (int32_t faceIdx = 0; faceIdx < faceCount; faceIdx++)
		{
			data.faces[faceIdx].vertices[0] = vertices[faceIdx * 3 + 0];
			data.faces[faceIdx].vertices[1] = vertices[faceIdx * 3 + 1];
			data.faces[faceIdx].vertices[2] = vertices[faceIdx * 3 + 2];
		}
	}
		break;
	case 4:
	{
		std::vector<uint32_t> vertices(faceCount * 3);
		file.read(reinterpret_cast<char*>(vertices.data()), sizeof(uint32_t) * vertices.size());
		for (int32_t faceIdx = 0; faceIdx < faceCount; faceIdx++)
		{
			data.faces[faceIdx].vertices[0] = vertices[faceIdx * 3 + 0];
			data.faces[faceIdx].vertices[1] = vertices[faceIdx * 3 + 1];
			data.faces[faceIdx].vertices[2] = vertices[faceIdx * 3 + 2];
		}
	}
		break;
	default:
			return false;
}

return true;
```
インデックス値のバイト数もヘッダーに含まれていました。そのサイズだけ読み込めるようにします。
PMXのインデックス値のバイト数は1、2、4バイトのいずれかです。

# テクスチャ情報の読み込み
メソッドは次のとおりです。
```cpp
bool ReadTextures(PMXFileData& data, std::ifstream& file)
{
    unsigned int numOfTexture = 0;
	file.read(reinterpret_cast<char*>(&numOfTexture), 4);

	data.textures.resize(numOfTexture);

	for (auto& texture : data.textures)
	{
		UnicodeUtil::GetPMXStringUTF16(file, texture.textureName);
	}

	return true;
}
```
最初の4バイトはテクスチャの数です。
テクスチャの数だけテクスチャの名前をstd::wstringとして読み込めるようにします。

# マテリアル情報の読み込み
UnityやUnreal Engineを扱ったことがある方なら、マテリアルという言葉は馴染みがあるでしょう。
マテリアル構造体は次のとおりです。
```cpp
struct PMXMaterial
{
	std::wstring name;
	std::string englishName;

	DirectX::XMFLOAT4 diffuse;
	DirectX::XMFLOAT3 specular;
	float specularPower;
	DirectX::XMFLOAT3 ambient;

	PMXDrawModeFlags drawMode;

	DirectX::XMFLOAT4 edgeColor;
	float edgeSize;

	unsigned int textureIndex;
	unsigned int sphereTextureIndex;
	PMXSphereMode sphereMode;

	PMXToonMode toonMode;
	unsigned int toonTextureIndex;

	std::wstring memo;

	unsigned int numFaceVertices;
};
```

メソッドは次のとおりです。
```cpp
bool ReadMaterial(PMXFileData& data, std::ifstream& file)
{
	int numOfMaterial = 0;
	file.read(reinterpret_cast<char*>(&numOfMaterial), 4);

	data.materials.resize(numOfMaterial);

	for (auto& mat : data.materials)
	{
		UnicodeUtil::GetPMXStringUTF16(file, mat.name);
		UnicodeUtil::GetPMXStringUTF8(file, mat.englishName);

		file.read(reinterpret_cast<char*>(&mat.diffuse), 16);
		file.read(reinterpret_cast<char*>(&mat.specular), 12);
		file.read(reinterpret_cast<char*>(&mat.specularPower), 4);
		file.read(reinterpret_cast<char*>(&mat.ambient), 12);

		file.read(reinterpret_cast<char*>(&mat.drawMode), 1);

		file.read(reinterpret_cast<char*>(&mat.edgeColor), 16);
		file.read(reinterpret_cast<char*>(&mat.edgeSize), 4);

		file.read(reinterpret_cast<char*>(&mat.textureIndex), data.header.textureIndexSize);
		file.read(reinterpret_cast<char*>(&mat.sphereTextureIndex), data.header.textureIndexSize);
		file.read(reinterpret_cast<char*>(&mat.sphereMode), 1);

		file.read(reinterpret_cast<char*>(&mat.toonMode), 1);

		if (mat.toonMode == PMXToonMode::Separate)
		{
			file.read(reinterpret_cast<char*>(&mat.toonTextureIndex), data.header.textureIndexSize);
		}
		else if (mat.toonMode == PMXToonMode::Common)
		{
			file.read(reinterpret_cast<char*>(&mat.toonTextureIndex), 1);
		}
		else
		{
			return false;
		}

		UnicodeUtil::GetPMXStringUTF16(file, mat.memo);

		file.read(reinterpret_cast<char*>(&mat.numFaceVertices), 4);
	}

	return true;
}
```
この部分も実際に使用する際に各パラメータについて話します。

# ボーン情報の読み込み
ボーンもかなり複雑です。
ボーンはさまざまなオプションを持つことができます。
PMXモデルのボーンのオプションは以下の通りです。
```cpp
enum class PMXBoneFlags : uint16_t
{
	TargetShowMode = 0x0001,
	AllowRotate = 0x0002,
	AllowTranslate = 0x0004,
	Visible = 0x0008,
	AllowControl = 0x0010,
	IK = 0x0020,
	AppendLocal = 0x0080,
	AppendRotate = 0x0100,
	AppendTranslate = 0x0200,
	FixedAxis = 0x0400,
	LocalAxis = 0x800,
	DeformAfterPhysics = 0x1000,
	DeformOuterParent = 0x2000,
};
```

そして、IKで使用するPMXIKLinkも定義します。
```cpp
struct PMXIKLink
{
	unsigned int ikBoneIndex;
	unsigned char enableLimit;

	DirectX::XMFLOAT3 limitMin;
	DirectX::XMFLOAT3 limitMax;
};
```
ボーンの構造体は次のとおりです。
```cpp
struct PMXBone
{
	std::wstring name;
	std::string englishName;

	DirectX::XMFLOAT3 position;
	unsigned int parentBoneIndex;
	unsigned int deformDepth;

	PMXBoneFlags boneFlag;

	DirectX::XMFLOAT3 positionOffset;
	unsigned int linkBoneIndex;

	unsigned int appendBoneIndex;
	float appendWeight;

	DirectX::XMFLOAT3 fixedAxis;
	DirectX::XMFLOAT3 localXAxis;
	DirectX::XMFLOAT3 localZAxis;

	unsigned int keyValue;

	unsigned int ikTargetBoneIndex;
	unsigned int ikIterationCount;
	float ikLimit;

	std::vector<PMXIKLink> ikLinks;
};
```

メソッド全体は次のとおりです。
```cpp
bool ReadBone(PMXFileData& data, std::ifstream& file)
{
	unsigned int numOfBone;
	file.read(reinterpret_cast<char*>(&numOfBone), 4);

	data.bones.resize(numOfBone);

	for (auto& bone : data.bones)
	{
		UnicodeUtil::GetPMXStringUTF16(file, bone.name);
		UnicodeUtil::GetPMXStringUTF8(file, bone.englishName);

		file.read(reinterpret_cast<char*>(&bone.position), 12);
		file.read(reinterpret_cast<char*>(&bone.parentBoneIndex), data.header.boneIndexSize);
		file.read(reinterpret_cast<char*>(&bone.deformDepth), 4);

		file.read(reinterpret_cast<char*>(&bone.boneFlag), 2);

		if (((uint16_t)bone.boneFlag & (uint16_t)PMXBoneFlags::TargetShowMode) == 0)
		{
			file.read(reinterpret_cast<char*>(&bone.positionOffset), 12);
		}
		else
		{
			file.read(reinterpret_cast<char*>(&bone.linkBoneIndex), data.header.boneIndexSize);
		}

		if (((uint16_t)bone.boneFlag & (uint16_t)PMXBoneFlags::AppendRotate) ||
			((uint16_t)bone.boneFlag & (uint16_t)PMXBoneFlags::AppendTranslate))
		{
			file.read(reinterpret_cast<char*>(&bone.appendBoneIndex), data.header.boneIndexSize);
			file.read(reinterpret_cast<char*>(&bone.appendWeight), 4);
		}

		if ((uint16_t)bone.boneFlag & (uint16_t)PMXBoneFlags::FixedAxis)
		{
			file.read(reinterpret_cast<char*>(&bone.fixedAxis), 12);
		}

		if ((uint16_t)bone.boneFlag & (uint16_t)PMXBoneFlags::LocalAxis)
		{
			file.read(reinterpret_cast<char*>(&bone.localXAxis), 12);
			file.read(reinterpret_cast<char*>(&bone.localZAxis), 12);
		}

		if ((uint16_t)bone.boneFlag & (uint16_t)PMXBoneFlags::DeformOuterParent)
		{
			file.read(reinterpret_cast<char*>(&bone.keyValue), 4);
		}

		if ((uint16_t)bone.boneFlag & (uint16_t)PMXBoneFlags::IK)
		{
			file.read(reinterpret_cast<char*>(&bone.ikTargetBoneIndex), data.header.boneIndexSize);
			file.read(reinterpret_cast<char*>(&bone.ikIterationCount), 4);
			file.read(reinterpret_cast<char*>(&bone.ikLimit), 4);

			unsigned int linkCount = 0;
			file.read(reinterpret_cast<char*>(&linkCount), 4);

			bone.ikLinks.resize(linkCount);
			for (auto& ikLink : bone.ikLinks)
			{
				file.read(reinterpret_cast<char*>(&ikLink.ikBoneIndex), data.header.boneIndexSize);
				file.read(reinterpret_cast<char*>(&ikLink.enableLimit), 1);

				if (ikLink.enableLimit != 0)
				{
					file.read(reinterpret_cast<char*>(&ikLink.limitMin), 12);
					file.read(reinterpret_cast<char*>(&ikLink.limitMax), 12);
				}
			}
		}
	}

	return true;
}
```
ボーンは設定されているオプションに応じて、読み込むか読み込まない情報が異なります。
オプション値を読み込み、ビット演算でオプションに該当するかどうかを検査します。

# モーフ情報の読み込み
モーフは顔の表情などをコントロールするための情報です。アニメーションに関連していますね。
モーフには複数のタイプが存在します。
```cpp
enum class PMXMorphType : uint8_t
{
	Group,
	Position,
	Bone,
	UV,
	AddUV1,
	AddUV2,
	AddUV3,
	AddUV4,
	Material,
	Flip,
	Impluse,
};
```

そして、モーフはタイプによって使用される情報の構造が異なります。
```cpp
struct PMXMorph
{
	std::wstring name;
	std::string englishName;

	unsigned char controlPanel;
	PMXMorphType morphType;

	struct PositionMorph
	{
		unsigned int vertexIndex;
		DirectX::XMFLOAT3 position;
	};

	struct UVMorph
	{
		unsigned int vertexIndex;
		DirectX::XMFLOAT4 uv;
	};

	struct BoneMorph
	{
		unsigned int boneIndex;
		DirectX::XMFLOAT3 position;
		DirectX::XMFLOAT4 quaternion;
	};

	struct MaterialMorph
	{
		enum class OpType : uint8_t
		{
			Mul,
			Add,
		};

		unsigned int	materialIndex;
		OpType	opType;
		DirectX::XMFLOAT4 diffuse;
		DirectX::XMFLOAT3 specular;
		float specularPower;
		DirectX::XMFLOAT3 ambient;
		DirectX::XMFLOAT4 edgeColor;
		float edgeSize;
		DirectX::XMFLOAT4 textureFactor;
		DirectX::XMFLOAT4 sphereTextureFactor;
		DirectX::XMFLOAT4 toonTextureFactor;
	};

	struct GroupMorph
	{
		unsigned int morphIndex;
		float weight;
	};

	struct FlipMorph
	{
		unsigned int morphIndex;
		float weight;
	};

	struct ImpulseMorph
	{
		unsigned int rigidBodyIndex;
		unsigned char localFlag;	//0:OFF 1:ON
		DirectX::XMFLOAT3 translateVelocity;
		DirectX::XMFLOAT3 rotateTorque;
	};

	std::vector<PositionMorph> positionMorph;
	std::vector<UVMorph> uvMorph;
	std::vector<BoneMorph> boneMorph;
	std::vector<MaterialMorph> materialMorph;
	std::vector<GroupMorph> groupMorph;
	std::vector<FlipMorph> flipMorph;
	std::vector<ImpulseMorph> impulseMorph;
};
```

読み込むメソッドは次のとおりです。
```cpp
bool ReadMorph(PMXFileData& data, std::ifstream& file)
{
	unsigned int numOfMorph = 0;
	file.read(reinterpret_cast<char*>(&numOfMorph), 4);

	data.morphs.resize(numOfMorph);

	for (auto& morph : data.morphs)
	{
		UnicodeUtil::GetPMXStringUTF16(file, morph.name);
		UnicodeUtil::GetPMXStringUTF8(file, morph.englishName);

		file.read(reinterpret_cast<char*>(&morph.controlPanel), 1);
		file.read(reinterpret_cast<char*>(&morph.morphType), 1);

		unsigned int dataCount;
		file.read(reinterpret_cast<char*>(&dataCount), 4);

		if (morph.morphType == PMXMorphType::Position)
		{
			morph.positionMorph.resize(dataCount);
			for (auto& morphData : morph.positionMorph)
			{
				file.read(reinterpret_cast<char*>(&morphData.vertexIndex), data.header.vertexIndexSize);
				file.read(reinterpret_cast<char*>(&morphData.position), 12);
			}
		}
		else if (morph.morphType == PMXMorphType::UV ||
			     morph.morphType == PMXMorphType::AddUV1 || 
			     morph.morphType == PMXMorphType::AddUV2 || 
			     morph.morphType == PMXMorphType::AddUV3 || 
			     morph.morphType == PMXMorphType::AddUV4)
		{
			morph.uvMorph.resize(dataCount);
			for (auto& morphData : morph.uvMorph)
			{
				file.read(reinterpret_cast<char*>(&morphData.vertexIndex), data.header.vertexIndexSize);
				file.read(reinterpret_cast<char*>(&morphData.uv), 16);
			}
		}
		else if (morph.morphType == PMXMorphType::Bone)
		{
			morph.boneMorph.resize(dataCount);
			for (auto& morphData : morph.boneMorph)
			{
				file.read(reinterpret_cast<char*>(&morphData.boneIndex), data.header.boneIndexSize);
				file.read(reinterpret_cast<char*>(&morphData.position), 12);
				file.read(reinterpret_cast<char*>(&morphData.quaternion), 16);
			}
		}
		else if (morph.morphType == PMXMorphType::Material)
		{
			morph.materialMorph.resize(dataCount);
			for (auto& morphData : morph.materialMorph)
			{
				file.read(reinterpret_cast<char*>(&morphData.materialIndex), data.header.materialIndexSize);
				file.read(reinterpret_cast<char*>(&morphData.opType), 1);
				file.read(reinterpret_cast<char*>(&morphData.diffuse), 16);
				file.read(reinterpret_cast<char*>(&morphData.specular), 12);
				file.read(reinterpret_cast<char*>(&morphData.specularPower), 4);
				file.read(reinterpret_cast<char*>(&morphData.ambient), 12);
				file.read(reinterpret_cast<char*>(&morphData.edgeColor), 16);
				file.read(reinterpret_cast<char*>(&morphData.edgeSize), 4);
				file.read(reinterpret_cast<char*>(&morphData.textureFactor), 16);
				file.read(reinterpret_cast<char*>(&morphData.sphereTextureFactor), 16);
				file.read(reinterpret_cast<char*>(&morphData.toonTextureFactor), 16);
			}
		}
		else if (morph.morphType == PMXMorphType::Group)
		{
			morph.groupMorph.resize(dataCount);
			for (auto& morphData : morph.groupMorph)
			{
				file.read(reinterpret_cast<char*>(&morphData.morphIndex), data.header.morphIndexSize);
				file.read(reinterpret_cast<char*>(&morphData.weight), 4);
			}
		}
		else if (morph.morphType == PMXMorphType::Flip)
		{
			morph.flipMorph.resize(dataCount);
			for (auto& morphData : morph.flipMorph)
			{
				file.read(reinterpret_cast<char*>(&morphData.morphIndex), data.header.morphIndexSize);
				file.read(reinterpret_cast<char*>(&morphData.weight), 4);
			}
		}
		else if (morph.morphType == PMXMorphType::Impluse)
		{
			morph.impulseMorph.resize(dataCount);
			for (auto& morphData : morph.impulseMorph)
			{
				file.read(reinterpret_cast<char*>(&morphData.rigidBodyIndex), data.header.rigidBodyIndexSize);
				file.read(reinterpret_cast<char*>(&morphData.localFlag), 1);
				file.read(reinterpret_cast<char*>(&morphData.translateVelocity), 12);
				file.read(reinterpret_cast<char*>(&morphData.rotateTorque), 12);
			}
		}
		else
		{
			return false;
		}
	}

	return true;
}
```

# ディスプレイフレーム情報の読み込み
この情報は使用しないため、特に説明しません。
```cpp
struct PMXDisplayFrame
{
	std::wstring name;
	std::string englishName;

	enum class TargetType : uint8_t
	{
		BoneIndex,
		MorphIndex,
	};
	struct Target
	{
		TargetType type;
		unsigned int index;
	};

	enum class FrameType : uint8_t
	{
		DefaultFrame,
		SpecialFrame
	};

	FrameType flag;
	std::vector<Target> targets;
};
```
```cpp
bool ReadDisplayFrame(PMXFileData& data, std::ifstream& file)
{
	unsigned int numOfDisplayFrame = 0;
	file.read(reinterpret_cast<char*>(&numOfDisplayFrame), 4);

	data.displayFrames.resize(numOfDisplayFrame);

	for (auto& displayFrame : data.displayFrames)
	{
		UnicodeUtil::GetPMXStringUTF16(file, displayFrame.name);
		UnicodeUtil::GetPMXStringUTF8(file, displayFrame.englishName);

		file.read(reinterpret_cast<char*>(&displayFrame.flag), 1);

		unsigned int targetCount = 0;
		file.read(reinterpret_cast<char*>(&targetCount), 4);

		displayFrame.targets.resize(targetCount);
		for (auto& target : displayFrame.targets)
		{
			file.read(reinterpret_cast<char*>(&target.type), 1);
			if (target.type == PMXDisplayFrame::TargetType::BoneIndex)
			{
				file.read(reinterpret_cast<char*>(&target.index), data.header.boneIndexSize);
			}
			else if (target.type == PMXDisplayFrame::TargetType::MorphIndex)
			{
				file.read(reinterpret_cast<char*>(&target.index), data.header.morphIndexSize);
			}
			else
			{
				return false;
			}
		}
	}

	return true;
}
```

# 剛体、ジョイント、ソフトボディ情報の読み込み
これらはすべて物理シミュレーションに関連する情報です。

今回はこれらが何であるかを説明すると長くなりすぎるため、後で物理エンジンを追加する際に詳しく見ていくことにします。今回は情報をどのように読み込むかだけを見ていきましょう。

剛体の構造体は次のとおりです。
```cpp
struct PMXRigidBody
{
	std::wstring name;
	std::string englishName;

	unsigned int boneIndex;
	unsigned char group;
	unsigned short collisionGroup;

	enum class Shape : uint8_t
	{
		Sphere,
		Box,
		Capsule
	};

	Shape shape;
	DirectX::XMFLOAT3 shapeSize;
	DirectX::XMFLOAT3 translate;
	DirectX::XMFLOAT3 rotate;

	float mass;
	float translateDimmer;
	float rotateDimmer;
	float repulsion;
	float friction;

	enum class Operation : uint8_t
	{
		Static,
		Dynamic,
		DynamicAndBoneMerge,
	};
	Operation op;
};
```
読み込むメソッドは次のとおりです。
```cpp
bool ReadRigidBody(PMXFileData& data, std::ifstream& file)
{
	unsigned int numOfRigidBody = 0;
	file.read(reinterpret_cast<char*>(&numOfRigidBody), 4);

	data.rigidBodies.resize(numOfRigidBody);

	for (auto& rigidBody : data.rigidBodies)
	{
		UnicodeUtil::GetPMXStringUTF16(file, rigidBody.name);
		UnicodeUtil::GetPMXStringUTF8(file, rigidBody.englishName);

		file.read(reinterpret_cast<char*>(&rigidBody.boneIndex), data.header.boneIndexSize);
		file.read(reinterpret_cast<char*>(&rigidBody.group), 1);
		file.read(reinterpret_cast<char*>(&rigidBody.collisionGroup), 2);
		file.read(reinterpret_cast<char*>(&rigidBody.shape), 1);
		file.read(reinterpret_cast<char*>(&rigidBody.shapeSize), 12);
		file.read(reinterpret_cast<char*>(&rigidBody.translate), 12);
		file.read(reinterpret_cast<char*>(&rigidBody.rotate), 12);
		file.read(reinterpret_cast<char*>(&rigidBody.mass), 4);
		file.read(reinterpret_cast<char*>(&rigidBody.translateDimmer), 4);
		file.read(reinterpret_cast<char*>(&rigidBody.rotateDimmer), 4);
		file.read(reinterpret_cast<char*>(&rigidBody.repulsion), 4);
		file.read(reinterpret_cast<char*>(&rigidBody.friction), 4);
		file.read(reinterpret_cast<char*>(&rigidBody.op), 1);
	}

	return true;
}
```

ジョイントの構造体は次のとおりです。
```cpp
struct PMXJoint
{
	std::wstring name;
	std::string englishName;

	enum class JointType : uint8_t
	{
		SpringDOF6,
		DOF6,
		P2P,
		ConeTwist,
		Slider,
		Hinge,
	};
	JointType type;
	unsigned int rigidBodyAIndex;
	unsigned int rigidBodyBIndex;

	DirectX::XMFLOAT3 translate;
	DirectX::XMFLOAT3 rotate;

	DirectX::XMFLOAT3 translateLowerLimit;
	DirectX::XMFLOAT3 translateUpperLimit;
	DirectX::XMFLOAT3 rotateLowerLimit;
	DirectX::XMFLOAT3 rotateUpperLimit;

	DirectX::XMFLOAT3 springTranslateFactor;
	DirectX::XMFLOAT3 springRotateFactor;
};
```

読み込むメソッドは次のとおりです。
```cpp
bool ReadJoint(PMXFileData& data, std::ifstream& file)
{
	unsigned int numOfJoint = 0;
	file.read(reinterpret_cast<char*>(&numOfJoint), 4);

	data.joints.resize(numOfJoint);

	for (auto& joint : data.joints)
	{
		UnicodeUtil::GetPMXStringUTF16(file, joint.name);
		UnicodeUtil::GetPMXStringUTF8(file, joint.englishName);

		file.read(reinterpret_cast<char*>(&joint.type), 1);
		file.read(reinterpret_cast<char*>(&joint.rigidBodyAIndex), data.header.rigidBodyIndexSize);
		file.read(reinterpret_cast<char*>(&joint.rigidBodyBIndex), data.header.rigidBodyIndexSize);

		file.read(reinterpret_cast<char*>(&joint.translate), 12);
		file.read(reinterpret_cast<char*>(&joint.rotate), 12);

		file.read(reinterpret_cast<char*>(&joint.translateLowerLimit), 12);
		file.read(reinterpret_cast<char*>(&joint.translateUpperLimit), 12);
		file.read(reinterpret_cast<char*>(&joint.rotateLowerLimit), 12);
		file.read(reinterpret_cast<char*>(&joint.rotateUpperLimit), 12);

		file.read(reinterpret_cast<char*>(&joint.springTranslateFactor), 12);
		file.read(reinterpret_cast<char*>(&joint.springRotateFactor), 12);
	}

	return true;
}
```

ソフトボディの構造体は次のとおりです。
```cpp
struct PMXSoftBody
{
	std::wstring name;
	std::string englishName;

	enum class SoftBodyType : uint8_t
	{
		TriMesh,
		Rope
	};
	SoftBodyType type;

	unsigned int materialIndex;
	unsigned char group;
	unsigned short collisionGroup;

	enum class SoftBodyMask : uint8_t
	{
		BLink = 0x01,
		Cluster = 0x02,
		HybridLink = 0x04,
	};
	SoftBodyMask flag;

	unsigned int bLinkLength;
	unsigned int numClusters;

	float totalMass;
	float collisionMargin;

	enum class AeroModel : int32_t
	{
		kAeroModelV_TwoSided,
		kAeroModelV_OneSided,
		kAeroModelF_TwoSided,
		kAeroModelF_OneSided,
	};
	unsigned int areoModel;

	float vcf;
	float dp;
	float dg;
	float lf;
	float pr;
	float vc;
	float df;
	float mt;
	float chr;
	float khr;
	float shr;
	float ahr;

	float srhr_cl;
	float skhr_cl;
	float sshr_cl;
	float sr_splt_cl;
	float sk_splt_cl;
	float ss_splt_cl;

	unsigned int v_it;
	unsigned int p_it;
	unsigned int d_it;
	unsigned int c_it;

	float lst;
	float ast;
	float vst;

	struct AnchorRigidBody
	{
		unsigned int rigidBodyIndex;
		unsigned int vertexIndex;
		unsigned char nearMode;
	};
	std::vector<AnchorRigidBody> anchorRigidBodies;

	std::vector<unsigned int> pinVertexIndices;
};
```

読み込むメソッドは次のとおりです。
```cpp
bool ReadSoftBody(PMXFileData& data, std::ifstream& file)
{
	unsigned int numOfSoftBody = 0;
	file.read(reinterpret_cast<char*>(&numOfSoftBody), 4);

	data.softBodies.resize(numOfSoftBody);

	for (auto& softBody : data.softBodies)
	{
		UnicodeUtil::GetPMXStringUTF16(file, softBody.name);
		UnicodeUtil::GetPMXStringUTF8(file, softBody.englishName);

		file.read(reinterpret_cast<char*>(&softBody.type), 1);

		file.read(reinterpret_cast<char*>(&softBody.materialIndex), data.header.materialIndexSize);
		file.read(reinterpret_cast<char*>(&softBody.group), 1);
		file.read(reinterpret_cast<char*>(&softBody.collisionGroup), 2);

		file.read(reinterpret_cast<char*>(&softBody.flag), 1);

		file.read(reinterpret_cast<char*>(&softBody.bLinkLength), 4);
		file.read(reinterpret_cast<char*>(&softBody.numClusters), 4);

		file.read(reinterpret_cast<char*>(&softBody.totalMass), 4);
		file.read(reinterpret_cast<char*>(&softBody.collisionMargin), 4);

		file.read(reinterpret_cast<char*>(&softBody.areoModel), 4);

		file.read(reinterpret_cast<char*>(&softBody.vcf), 4);
		file.read(reinterpret_cast<char*>(&softBody.dp), 4);
		file.read(reinterpret_cast<char*>(&softBody.dg), 4);
		file.read(reinterpret_cast<char*>(&softBody.lf) , 4);
		file.read(reinterpret_cast<char*>(&softBody.pr), 4);
		file.read(reinterpret_cast<char*>(&softBody.vc), 4);
		file.read(reinterpret_cast<char*>(&softBody.df), 4);
		file.read(reinterpret_cast<char*>(&softBody.mt), 4);
		file.read(reinterpret_cast<char*>(&softBody.chr), 4);
		file.read(reinterpret_cast<char*>(&softBody.khr), 4);
		file.read(reinterpret_cast<char*>(&softBody.shr), 4);
		file.read(reinterpret_cast<char*>(&softBody.ahr), 4);

		file.read(reinterpret_cast<char*>(&softBody.srhr_cl), 4);
		file.read(reinterpret_cast<char*>(&softBody.skhr_cl), 4);
		file.read(reinterpret_cast<char*>(&softBody.sshr_cl), 4);
		file.read(reinterpret_cast<char*>(&softBody.sr_splt_cl), 4);
		file.read(reinterpret_cast<char*>(&softBody.sk_splt_cl), 4);
		file.read(reinterpret_cast<char*>(&softBody.ss_splt_cl), 4);

		file.read(reinterpret_cast<char*>(&softBody.v_it), 4);
		file.read(reinterpret_cast<char*>(&softBody.p_it), 4);
		file.read(reinterpret_cast<char*>(&softBody.d_it), 4);
		file.read(reinterpret_cast<char*>(&softBody.c_it), 4);

		file.read(reinterpret_cast<char*>(&softBody.lst), 4);
		file.read(reinterpret_cast<char*>(&softBody.ast), 4);
		file.read(reinterpret_cast<char*>(&softBody.vst), 4);

		unsigned int anchorCount = 0;
		file.read(reinterpret_cast<char*>(&anchorCount), 4);

		softBody.anchorRigidBodies.resize(anchorCount);
		for (auto& anchor : softBody.anchorRigidBodies)
		{
			file.read(reinterpret_cast<char*>(&anchor.rigidBodyIndex), data.header.rigidBodyIndexSize);
			file.read(reinterpret_cast<char*>(&anchor.vertexIndex), data.header.vertexIndexSize);
			file.read(reinterpret_cast<char*>(&anchor.nearMode), 1);
		}

		unsigned int pinVertexCount = 0;
		file.read(reinterpret_cast<char*>(&pinVertexCount), 4);

		softBody.pinVertexIndices.resize(pinVertexCount);
		for (auto& pinVertex : softBody.pinVertexIndices)
		{
			file.read(reinterpret_cast<char*>(&pinVertex), data.header.vertexIndexSize);
		}
	}

	return true;
}
```

# データの確認
ここまで準備ができたら、実際にファイルを読み込んでデータが正しく読み取れているか確認してください。

コード自体が長いため、見落としている箇所があるかもしれませんが、問題がなければデータが正しく読み込んでいることを確認できるはずです。
```cpp
bool LoadPMXFile(const std::wstring& filePath, PMXFileData& fileData)
{
	if (filePath.empty() == true)
	{
		return false;
	}

	std::ifstream pmxFile{ filePath, (std::ios::binary | std::ios::in) };
	if (pmxFile.fail())
	{
		pmxFile.close();
		return false;
	}

	bool result = ReadHeader(fileData, pmxFile);
	if (result == false)
	{
		return false;
	}

	result = ReadModelInfo(fileData, pmxFile);
	if (result == false)
	{
		return false;
	}

	result = ReadVertex(fileData, pmxFile);
	if (result == false)
	{
		return false;
	}

	result = ReadFace(fileData, pmxFile);
	if (result == false)
	{
		return false;
	}

	result = ReadTextures(fileData, pmxFile);
	if (result == false)
	{
		return false;
	}

	result = ReadMaterial(fileData, pmxFile);
	if (result == false)
	{
		return false;
	}

	result = ReadBone(fileData, pmxFile);
	if (result == false)
	{
		return false;
	}

	result = ReadMorph(fileData, pmxFile);
	if (result == false)
	{
		return false;
	}

	result = ReadDisplayFrame(fileData, pmxFile);
	if (result == false)
	{
		return false;
	}

	result = ReadRigidBody(fileData, pmxFile);
	if (result == false)
	{
		return false;
	}

	result = ReadJoint(fileData, pmxFile);
	if (result == false)
	{
		return false;
	}

	result = ReadSoftBody(fileData, pmxFile);
	if (result == false)
	{
		return false;
	}

	pmxFile.close();

	return true;
}
```

# まとめ
今回は、PMXモデルファイルがどのように構成されているか、そしてどのように読み込むべきかを学びました。
特に考えるべき点はなく、既存のフォーマットを読み込むだけなので、退屈な作業です。

後半に出てくる物理シミュレーションに関連するデータについては、特に説明しませんでした。内容が非常に多く、実際に適用するまでどのような役割を果たすデータなのか理解するのが難しいです。そのため、後で実際に使用する際にまた話すことにします。

次の記事では、読み込んだPMXデータを使ってミクさんを画面に描画してみましょう。

# 次回
https://qiita.com/dpals39/items/ea06391fc3aad92b2b65