#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER
#include "MMDImporter.h"
#include "MMDPmdParser.h"
#include "MMDPmxParser.h"
#include "MMDVmdParser.h"
#include "ConvertToLHProcess.h"
#include <assimp/DefaultIOSystem.h>
#include <assimp/Importer.hpp>
#include <assimp/ai_assert.h>
#include <assimp/scene.h>
#include <fstream>
#include <iomanip>
#include <memory>
static const aiImporterDesc desc = {"MMD Importer",
"",
"",
"surfaces supported?",
aiImporterFlags_SupportTextFlavour,
0,
0,
0,
0,
"pmx"};
namespace Assimp {
using namespace std;
MMDImporter::MMDImporter()
: m_Buffer(),
m_strAbsPath("") {
DefaultIOSystem io;
m_strAbsPath = io.getOsSeparator();
}
MMDImporter::~MMDImporter() {
}
bool MMDImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler,
bool checkSig) const {
if (!checkSig) {
return SimpleExtensionCheck(pFile, "pmx");
} else {
static const char *pTokens[] = {"PMX "};
return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, pTokens,
1);
}
}
const aiImporterDesc *MMDImporter::GetInfo() const { return &desc; }
void MMDImporter::InternReadFile(const std::string &file, aiScene *pScene,
IOSystem *pIOHandler) {
std::filebuf fb;
if (!fb.open(file, std::ios::in | std::ios::binary)) {
throw DeadlyImportError("Failed to open file " + file + ".");
}
std::istream fileStream(&fb);
fileStream.seekg(0, fileStream.end);
size_t fileSize = fileStream.tellg();
fileStream.seekg(0, fileStream.beg);
if (fileSize < sizeof(pmx::PmxModel)) {
throw DeadlyImportError(file + " is too small.");
}
pmx::PmxModel model;
model.Read(&fileStream);
CreateDataFromImport(&model, pScene);
}
void MMDImporter::CreateDataFromImport(const pmx::PmxModel *pModel,
aiScene *pScene) {
if (pModel == NULL) {
return;
}
aiNode *pNode = new aiNode;
if (!pModel->model_name.empty()) {
pNode->mName.Set(pModel->model_name);
} else {
ai_assert(false);
}
pScene->mRootNode = pNode;
pNode = new aiNode;
pScene->mRootNode->addChildren(1, &pNode);
pNode->mName.Set(string(pModel->model_name) + string("_mesh"));
pNode->mNumMeshes = pModel->material_count;
pNode->mMeshes = new unsigned int[pNode->mNumMeshes];
for (unsigned int index = 0; index < pNode->mNumMeshes; index++) {
pNode->mMeshes[index] = index;
}
pScene->mNumMeshes = pModel->material_count;
pScene->mMeshes = new aiMesh *[pScene->mNumMeshes];
for (unsigned int i = 0, indexStart = 0; i < pScene->mNumMeshes; i++) {
const int indexCount = pModel->materials[i].index_count;
pScene->mMeshes[i] = CreateMesh(pModel, indexStart, indexCount);
pScene->mMeshes[i]->mName = pModel->materials[i].material_name;
pScene->mMeshes[i]->mMaterialIndex = i;
indexStart += indexCount;
}
aiNode **ppNode = new aiNode *[pModel->bone_count];
for (auto i = 0; i < pModel->bone_count; i++) {
ppNode[i] = new aiNode(pModel->bones[i].bone_name);
}
for (auto i = 0; i < pModel->bone_count; i++) {
const pmx::PmxBone &bone = pModel->bones[i];
if (bone.parent_index < 0) {
pScene->mRootNode->addChildren(1, ppNode + i);
} else {
ppNode[bone.parent_index]->addChildren(1, ppNode + i);
aiVector3D v3 = aiVector3D(
bone.position[0] - pModel->bones[bone.parent_index].position[0],
bone.position[1] - pModel->bones[bone.parent_index].position[1],
bone.position[2] - pModel->bones[bone.parent_index].position[2]);
aiMatrix4x4::Translation(v3, ppNode[i]->mTransformation);
}
}
pScene->mNumMaterials = pModel->material_count;
pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials];
for (unsigned int i = 0; i < pScene->mNumMaterials; i++) {
pScene->mMaterials[i] = CreateMaterial(&pModel->materials[i], pModel);
}
MakeLeftHandedProcess convertProcess;
convertProcess.Execute(pScene);
FlipUVsProcess uvFlipper;
uvFlipper.Execute(pScene);
FlipWindingOrderProcess windingFlipper;
windingFlipper.Execute(pScene);
}
aiMesh *MMDImporter::CreateMesh(const pmx::PmxModel *pModel,
const int indexStart, const int indexCount) {
aiMesh *pMesh = new aiMesh;
pMesh->mNumVertices = indexCount;
pMesh->mNumFaces = indexCount / 3;
pMesh->mFaces = new aiFace[pMesh->mNumFaces];
const int numIndices = 3; for (unsigned int index = 0; index < pMesh->mNumFaces; index++) {
pMesh->mFaces[index].mNumIndices = numIndices;
unsigned int *indices = new unsigned int[numIndices];
indices[0] = numIndices * index;
indices[1] = numIndices * index + 1;
indices[2] = numIndices * index + 2;
pMesh->mFaces[index].mIndices = indices;
}
pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
pMesh->mTextureCoords[0] = new aiVector3D[pMesh->mNumVertices];
pMesh->mNumUVComponents[0] = 2;
for (int i = 1; i <= pModel->setting.uv; i++) {
pMesh->mTextureCoords[i] = new aiVector3D[pMesh->mNumVertices];
pMesh->mNumUVComponents[i] = 4;
}
map<int, vector<aiVertexWeight>> bone_vertex_map;
for (int index = 0; index < indexCount; index++) {
const pmx::PmxVertex *v =
&pModel->vertices[pModel->indices[indexStart + index]];
const float *position = v->position;
pMesh->mVertices[index].Set(position[0], position[1], position[2]);
const float *normal = v->normal;
pMesh->mNormals[index].Set(normal[0], normal[1], normal[2]);
pMesh->mTextureCoords[0][index].x = v->uv[0];
pMesh->mTextureCoords[0][index].y = v->uv[1];
for (int i = 1; i <= pModel->setting.uv; i++) {
pMesh->mTextureCoords[i][index].x = v->uva[i][0];
pMesh->mTextureCoords[i][index].y = v->uva[i][1];
}
const auto vsBDEF1_ptr =
dynamic_cast<pmx::PmxVertexSkinningBDEF1 *>(v->skinning.get());
const auto vsBDEF2_ptr =
dynamic_cast<pmx::PmxVertexSkinningBDEF2 *>(v->skinning.get());
const auto vsBDEF4_ptr =
dynamic_cast<pmx::PmxVertexSkinningBDEF4 *>(v->skinning.get());
const auto vsSDEF_ptr =
dynamic_cast<pmx::PmxVertexSkinningSDEF *>(v->skinning.get());
switch (v->skinning_type) {
case pmx::PmxVertexSkinningType::BDEF1:
bone_vertex_map[vsBDEF1_ptr->bone_index].push_back(
aiVertexWeight(index, 1.0));
break;
case pmx::PmxVertexSkinningType::BDEF2:
bone_vertex_map[vsBDEF2_ptr->bone_index1].push_back(
aiVertexWeight(index, vsBDEF2_ptr->bone_weight));
bone_vertex_map[vsBDEF2_ptr->bone_index2].push_back(
aiVertexWeight(index, 1.0 - vsBDEF2_ptr->bone_weight));
break;
case pmx::PmxVertexSkinningType::BDEF4:
bone_vertex_map[vsBDEF4_ptr->bone_index1].push_back(
aiVertexWeight(index, vsBDEF4_ptr->bone_weight1));
bone_vertex_map[vsBDEF4_ptr->bone_index2].push_back(
aiVertexWeight(index, vsBDEF4_ptr->bone_weight2));
bone_vertex_map[vsBDEF4_ptr->bone_index3].push_back(
aiVertexWeight(index, vsBDEF4_ptr->bone_weight3));
bone_vertex_map[vsBDEF4_ptr->bone_index4].push_back(
aiVertexWeight(index, vsBDEF4_ptr->bone_weight4));
break;
case pmx::PmxVertexSkinningType::SDEF: bone_vertex_map[vsSDEF_ptr->bone_index1].push_back(
aiVertexWeight(index, vsSDEF_ptr->bone_weight));
bone_vertex_map[vsSDEF_ptr->bone_index2].push_back(
aiVertexWeight(index, 1.0 - vsSDEF_ptr->bone_weight));
break;
case pmx::PmxVertexSkinningType::QDEF:
const auto vsQDEF_ptr =
dynamic_cast<pmx::PmxVertexSkinningQDEF *>(v->skinning.get());
bone_vertex_map[vsQDEF_ptr->bone_index1].push_back(
aiVertexWeight(index, vsQDEF_ptr->bone_weight1));
bone_vertex_map[vsQDEF_ptr->bone_index2].push_back(
aiVertexWeight(index, vsQDEF_ptr->bone_weight2));
bone_vertex_map[vsQDEF_ptr->bone_index3].push_back(
aiVertexWeight(index, vsQDEF_ptr->bone_weight3));
bone_vertex_map[vsQDEF_ptr->bone_index4].push_back(
aiVertexWeight(index, vsQDEF_ptr->bone_weight4));
break;
}
}
auto bone_ptr_ptr = new aiBone *[pModel->bone_count];
pMesh->mNumBones = pModel->bone_count;
pMesh->mBones = bone_ptr_ptr;
for (auto ii = 0; ii < pModel->bone_count; ++ii) {
auto pBone = new aiBone;
const auto &pmxBone = pModel->bones[ii];
pBone->mName = pmxBone.bone_name;
aiVector3D pos(pmxBone.position[0], pmxBone.position[1], pmxBone.position[2]);
aiMatrix4x4::Translation(-pos, pBone->mOffsetMatrix);
auto it = bone_vertex_map.find(ii);
if (it != bone_vertex_map.end()) {
pBone->mNumWeights = it->second.size();
pBone->mWeights = it->second.data();
it->second.swap(*(new vector<aiVertexWeight>));
}
bone_ptr_ptr[ii] = pBone;
}
return pMesh;
}
aiMaterial *MMDImporter::CreateMaterial(const pmx::PmxMaterial *pMat,
const pmx::PmxModel *pModel) {
aiMaterial *mat = new aiMaterial();
aiString name(pMat->material_english_name);
mat->AddProperty(&name, AI_MATKEY_NAME);
aiColor3D diffuse(pMat->diffuse[0], pMat->diffuse[1], pMat->diffuse[2]);
mat->AddProperty(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
aiColor3D specular(pMat->specular[0], pMat->specular[1], pMat->specular[2]);
mat->AddProperty(&specular, 1, AI_MATKEY_COLOR_SPECULAR);
aiColor3D ambient(pMat->ambient[0], pMat->ambient[1], pMat->ambient[2]);
mat->AddProperty(&ambient, 1, AI_MATKEY_COLOR_AMBIENT);
float opacity = pMat->diffuse[3];
mat->AddProperty(&opacity, 1, AI_MATKEY_OPACITY);
float shininess = pMat->specularlity;
mat->AddProperty(&shininess, 1, AI_MATKEY_SHININESS_STRENGTH);
aiString texture_path(pModel->textures[pMat->diffuse_texture_index]);
mat->AddProperty(&texture_path, AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0));
int mapping_uvwsrc = 0;
mat->AddProperty(&mapping_uvwsrc, 1,
AI_MATKEY_UVWSRC(aiTextureType_DIFFUSE, 0));
return mat;
}
}
#endif