diff --git a/cmake-modules/FindDevIL.cmake b/cmake-modules/FindDevIL.cmake new file mode 100644 index 000000000..381a75dd2 --- /dev/null +++ b/cmake-modules/FindDevIL.cmake @@ -0,0 +1,72 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#.rst: +# FindDevIL +# --------- +# +# +# +# This module locates the developer's image library. +# http://openil.sourceforge.net/ +# +# This module sets: +# +# :: +# +# IL_LIBRARIES - the name of the IL library. These include the full path to +# the core DevIL library. This one has to be linked into the +# application. +# ILU_LIBRARIES - the name of the ILU library. Again, the full path. This +# library is for filters and effects, not actual loading. It +# doesn't have to be linked if the functionality it provides +# is not used. +# ILUT_LIBRARIES - the name of the ILUT library. Full path. This part of the +# library interfaces with OpenGL. It is not strictly needed +# in applications. +# IL_INCLUDE_DIR - where to find the il.h, ilu.h and ilut.h files. +# IL_FOUND - this is set to TRUE if all the above variables were set. +# This will be set to false if ILU or ILUT are not found, +# even if they are not needed. In most systems, if one +# library is found all the others are as well. That's the +# way the DevIL developers release it. + +# TODO: Add version support. +# Tested under Linux and Windows (MSVC) + +#include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) +include(FindPackageHandleStandardArgs) + +find_path(IL_INCLUDE_DIR il.h + PATH_SUFFIXES include IL + DOC "The path to the directory that contains il.h" +) + +#message("IL_INCLUDE_DIR is ${IL_INCLUDE_DIR}") + +find_library(IL_LIBRARIES + NAMES IL DEVIL + PATH_SUFFIXES lib64 lib lib32 + DOC "The file that corresponds to the base il library." +) + +#message("IL_LIBRARIES is ${IL_LIBRARIES}") + +find_library(ILUT_LIBRARIES + NAMES ILUT + PATH_SUFFIXES lib64 lib lib32 + DOC "The file that corresponds to the il (system?) utility library." +) + +#message("ILUT_LIBRARIES is ${ILUT_LIBRARIES}") + +find_library(ILU_LIBRARIES + NAMES ILU + PATH_SUFFIXES lib64 lib lib32 + DOC "The file that corresponds to the il utility library." +) + +#message("ILU_LIBRARIES is ${ILU_LIBRARIES}") + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(IL DEFAULT_MSG + IL_LIBRARIES IL_INCLUDE_DIR) diff --git a/code/AssbinLoader.cpp b/code/AssbinLoader.cpp index d5a50b40a..3ed58297c 100644 --- a/code/AssbinLoader.cpp +++ b/code/AssbinLoader.cpp @@ -198,7 +198,7 @@ template void ReadBounds( IOStream * stream, T* /*p*/, unsigned int stream->Seek( sizeof(T) * n, aiOrigin_CUR ); } -void AssbinImporter::ReadBinaryNode( IOStream * stream, aiNode** node ) +void AssbinImporter::ReadBinaryNode( IOStream * stream, aiNode** node, aiNode* parent ) { uint32_t chunkID = Read(stream); ai_assert(chunkID == ASSBIN_CHUNK_AINODE); @@ -210,6 +210,10 @@ void AssbinImporter::ReadBinaryNode( IOStream * stream, aiNode** node ) (*node)->mTransformation = Read(stream); (*node)->mNumChildren = Read(stream); (*node)->mNumMeshes = Read(stream); + if(parent) + { + (*node)->mParent = parent; + } if ((*node)->mNumMeshes) { @@ -223,7 +227,7 @@ void AssbinImporter::ReadBinaryNode( IOStream * stream, aiNode** node ) { (*node)->mChildren = new aiNode*[(*node)->mNumChildren]; for (unsigned int i = 0; i < (*node)->mNumChildren; ++i) { - ReadBinaryNode( stream, &(*node)->mChildren[i] ); + ReadBinaryNode( stream, &(*node)->mChildren[i], *node ); } } @@ -571,7 +575,7 @@ void AssbinImporter::ReadBinaryScene( IOStream * stream, aiScene* scene ) // Read node graph scene->mRootNode = new aiNode[1]; - ReadBinaryNode( stream, &scene->mRootNode ); + ReadBinaryNode( stream, &scene->mRootNode, (aiNode*)NULL ); // Read all meshes if (scene->mNumMeshes) diff --git a/code/AssbinLoader.h b/code/AssbinLoader.h index ece67c983..fc28d1667 100644 --- a/code/AssbinLoader.h +++ b/code/AssbinLoader.h @@ -86,7 +86,7 @@ public: IOSystem* pIOHandler ); void ReadBinaryScene( IOStream * stream, aiScene* pScene ); - void ReadBinaryNode( IOStream * stream, aiNode** mRootNode ); + void ReadBinaryNode( IOStream * stream, aiNode** mRootNode, aiNode* parent ); void ReadBinaryMesh( IOStream * stream, aiMesh* mesh ); void ReadBinaryBone( IOStream * stream, aiBone* bone ); void ReadBinaryMaterial(IOStream * stream, aiMaterial* mat); diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 8960ea32c..de515ba94 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -667,6 +667,16 @@ ADD_ASSIMP_IMPORTER( 3MF D3MFOpcPackage.cpp ) +ADD_ASSIMP_IMPORTER( MMD + MMDCpp14.h + MMDImporter.cpp + MMDImporter.h + MMDPmdParser.h + MMDPmxParser.h + MMDPmxParser.cpp + MMDVmdParser.h +) + SET( Step_SRCS StepExporter.h StepExporter.cpp diff --git a/code/ColladaExporter.cpp b/code/ColladaExporter.cpp index 3966d0d79..1b8a60883 100644 --- a/code/ColladaExporter.cpp +++ b/code/ColladaExporter.cpp @@ -58,6 +58,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include +#include using namespace Assimp; @@ -132,6 +134,7 @@ void ColladaExporter::WriteFile() WriteLightsLibrary(); WriteMaterials(); WriteGeometryLibrary(); + WriteControllerLibrary(); WriteSceneLibrary(); @@ -785,6 +788,177 @@ void ColladaExporter::WriteMaterials() } } +// ------------------------------------------------------------------------------------------------ +// Writes the controller library +void ColladaExporter::WriteControllerLibrary() +{ + mOutput << startstr << "" << endstr; + PushTag(); + + for( size_t a = 0; a < mScene->mNumMeshes; ++a) + WriteController( a); + + PopTag(); + mOutput << startstr << "" << endstr; +} + +// ------------------------------------------------------------------------------------------------ +// Writes a skin controller of the given mesh +void ColladaExporter::WriteController( size_t pIndex) +{ + const aiMesh* mesh = mScene->mMeshes[pIndex]; + const std::string idstr = GetMeshId( pIndex); + const std::string idstrEscaped = XMLEscape(idstr); + + if ( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 ) + return; + + if ( mesh->mNumBones == 0 ) + return; + + mOutput << startstr << ""<< endstr; + PushTag(); + + mOutput << startstr << "" << endstr; + PushTag(); + + // bind pose matrix + mOutput << startstr << "" << endstr; + PushTag(); + + // I think it is identity in general cases. + aiMatrix4x4 mat; + mOutput << startstr << mat.a1 << " " << mat.a2 << " " << mat.a3 << " " << mat.a4 << endstr; + mOutput << startstr << mat.b1 << " " << mat.b2 << " " << mat.b3 << " " << mat.b4 << endstr; + mOutput << startstr << mat.c1 << " " << mat.c2 << " " << mat.c3 << " " << mat.c4 << endstr; + mOutput << startstr << mat.d1 << " " << mat.d2 << " " << mat.d3 << " " << mat.d4 << endstr; + + PopTag(); + mOutput << startstr << "" << endstr; + + mOutput << startstr << "" << endstr; + PushTag(); + + mOutput << startstr << "mNumBones << "\">"; + + for( size_t i = 0; i < mesh->mNumBones; ++i ) + mOutput << XMLEscape(mesh->mBones[i]->mName.C_Str()) << " "; + + mOutput << "" << endstr; + + mOutput << startstr << "" << endstr; + PushTag(); + + mOutput << startstr << "mNumBones << "\" stride=\"" << 1 << "\">" << endstr; + PushTag(); + + mOutput << startstr << "" << endstr; + + PopTag(); + mOutput << startstr << "" << endstr; + + PopTag(); + mOutput << startstr << "" << endstr; + + PopTag(); + mOutput << startstr << "" << endstr; + + std::vector bind_poses; + bind_poses.reserve(mesh->mNumBones * 16); + for( size_t i = 0; i < mesh->mNumBones; ++i) + for( size_t j = 0; j < 4; ++j) + bind_poses.insert(bind_poses.end(), mesh->mBones[i]->mOffsetMatrix[j], mesh->mBones[i]->mOffsetMatrix[j] + 4); + + WriteFloatArray( idstr + "-skin-bind_poses", FloatType_Mat4x4, (const ai_real*) bind_poses.data(), bind_poses.size() / 16); + + bind_poses.clear(); + + std::vector skin_weights; + skin_weights.reserve(mesh->mNumVertices * mesh->mNumBones); + for( size_t i = 0; i < mesh->mNumBones; ++i) + for( size_t j = 0; j < mesh->mBones[i]->mNumWeights; ++j) + skin_weights.push_back(mesh->mBones[i]->mWeights[j].mWeight); + + WriteFloatArray( idstr + "-skin-weights", FloatType_Weight, (const ai_real*) skin_weights.data(), skin_weights.size()); + + skin_weights.clear(); + + mOutput << startstr << "" << endstr; + PushTag(); + + mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; + + PopTag(); + mOutput << startstr << "" << endstr; + + mOutput << startstr << "mNumVertices << "\">" << endstr; + PushTag(); + + mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; + + mOutput << startstr << ""; + + std::vector num_influences(mesh->mNumVertices, (ai_uint)0); + for( size_t i = 0; i < mesh->mNumBones; ++i) + for( size_t j = 0; j < mesh->mBones[i]->mNumWeights; ++j) + ++num_influences[mesh->mBones[i]->mWeights[j].mVertexId]; + + for( size_t i = 0; i < mesh->mNumVertices; ++i) + mOutput << num_influences[i] << " "; + + mOutput << "" << endstr; + + mOutput << startstr << ""; + + ai_uint joint_weight_indices_length = 0; + std::vector accum_influences; + accum_influences.reserve(num_influences.size()); + for( size_t i = 0; i < num_influences.size(); ++i) + { + accum_influences.push_back(joint_weight_indices_length); + joint_weight_indices_length += num_influences[i]; + } + + ai_uint weight_index = 0; + std::vector joint_weight_indices(2 * joint_weight_indices_length, (ai_int)-1); + for( size_t i = 0; i < mesh->mNumBones; ++i) + for( size_t j = 0; j < mesh->mBones[i]->mNumWeights; ++j) + { + unsigned int vId = mesh->mBones[i]->mWeights[j].mVertexId; + for( size_t k = 0; k < num_influences[vId]; ++k) + { + if (joint_weight_indices[2 * (accum_influences[vId] + k)] == -1) + { + joint_weight_indices[2 * (accum_influences[vId] + k)] = i; + joint_weight_indices[2 * (accum_influences[vId] + k) + 1] = weight_index; + break; + } + } + ++weight_index; + } + + for( size_t i = 0; i < joint_weight_indices.size(); ++i) + mOutput << joint_weight_indices[i] << " "; + + num_influences.clear(); + accum_influences.clear(); + joint_weight_indices.clear(); + + mOutput << "" << endstr; + + PopTag(); + mOutput << startstr << "" << endstr; + + PopTag(); + mOutput << startstr << "" << endstr; + + PopTag(); + mOutput << startstr << "" << endstr; +} + // ------------------------------------------------------------------------------------------------ // Writes the geometry library void ColladaExporter::WriteGeometryLibrary() @@ -949,6 +1123,8 @@ void ColladaExporter::WriteFloatArray( const std::string& pIdString, FloatDataTy case FloatType_TexCoord2: floatsPerElement = 2; break; case FloatType_TexCoord3: floatsPerElement = 3; break; case FloatType_Color: floatsPerElement = 3; break; + case FloatType_Mat4x4: floatsPerElement = 16; break; + case FloatType_Weight: floatsPerElement = 1; break; default: return; } @@ -1017,6 +1193,14 @@ void ColladaExporter::WriteFloatArray( const std::string& pIdString, FloatDataTy mOutput << startstr << "" << endstr; mOutput << startstr << "" << endstr; break; + + case FloatType_Mat4x4: + mOutput << startstr << "" << endstr; + break; + + case FloatType_Weight: + mOutput << startstr << "" << endstr; + break; } PopTag(); @@ -1078,16 +1262,24 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode) // If the node is associated with a bone, it is a joint node (JOINT) // otherwise it is a normal node (NODE) const char * node_type; + bool is_joint, is_skeleton_root = false; if (NULL == findBone(pScene, pNode->mName.C_Str())) { node_type = "NODE"; + is_joint = false; } else { node_type = "JOINT"; + is_joint = true; + if(!pNode->mParent || NULL == findBone(pScene, pNode->mParent->mName.C_Str())) + is_skeleton_root = true; } const std::string node_name_escaped = XMLEscape(pNode->mName.data); mOutput << startstr - << "" << endstr; PushTag(); @@ -1095,7 +1287,7 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode) // write transformation - we can directly put the matrix there // TODO: (thom) decompose into scale - rot - quad to allow addressing it by animations afterwards const aiMatrix4x4& mat = pNode->mTransformation; - mOutput << startstr << ""; + mOutput << startstr << ""; mOutput << mat.a1 << " " << mat.a2 << " " << mat.a3 << " " << mat.a4 << " "; mOutput << mat.b1 << " " << mat.b2 << " " << mat.b3 << " " << mat.b4 << " "; mOutput << mat.c1 << " " << mat.c2 << " " << mat.c3 << " " << mat.c4 << " "; @@ -1123,33 +1315,50 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode) for( size_t a = 0; a < pNode->mNumMeshes; ++a ) { const aiMesh* mesh = mScene->mMeshes[pNode->mMeshes[a]]; - // do not instanciate mesh if empty. I wonder how this could happen - if( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 ) - continue; - mOutput << startstr << "mMeshes[a])) << "\">" << endstr; - PushTag(); - mOutput << startstr << "" << endstr; - PushTag(); - mOutput << startstr << "" << endstr; - PushTag(); - mOutput << startstr << "mMaterialIndex].name) << "\">" << endstr; - PushTag(); - for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a ) - { - if( mesh->HasTextureCoords( static_cast(a) ) ) - // semantic as in - // input_semantic as in - // input_set as in - mOutput << startstr << "" << endstr; - } - PopTag(); - mOutput << startstr << "" << endstr; - PopTag(); - mOutput << startstr << "" << endstr; - PopTag(); - mOutput << startstr << "" << endstr; - PopTag(); - mOutput << startstr << "" << endstr; + // do not instanciate mesh if empty. I wonder how this could happen + if( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 ) + continue; + + if( mesh->mNumBones == 0 ) + { + mOutput << startstr << "mMeshes[a])) << "\">" << endstr; + PushTag(); + } + else + { + mOutput << startstr + << "mMeshes[a])) << "-skin\">" + << endstr; + PushTag(); + + mOutput << startstr << "#skeleton_root" << endstr; + } + mOutput << startstr << "" << endstr; + PushTag(); + mOutput << startstr << "" << endstr; + PushTag(); + mOutput << startstr << "mMaterialIndex].name) << "\">" << endstr; + PushTag(); + for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a ) + { + if( mesh->HasTextureCoords( static_cast(a) ) ) + // semantic as in + // input_semantic as in + // input_set as in + mOutput << startstr << "" << endstr; + } + PopTag(); + mOutput << startstr << "" << endstr; + PopTag(); + mOutput << startstr << "" << endstr; + PopTag(); + mOutput << startstr << "" << endstr; + + PopTag(); + if( mesh->mNumBones == 0) + mOutput << startstr << "" << endstr; + else + mOutput << startstr << "" << endstr; } // recurse into subnodes diff --git a/code/ColladaExporter.h b/code/ColladaExporter.h index cb1f31066..36c012f2e 100644 --- a/code/ColladaExporter.h +++ b/code/ColladaExporter.h @@ -102,13 +102,19 @@ protected: void WriteSpotLight(const aiLight *const light); void WriteAmbienttLight(const aiLight *const light); + /// Writes the controller library + void WriteControllerLibrary(); + + /// Writes a skin controller of the given mesh + void WriteController( size_t pIndex); + /// Writes the geometry library void WriteGeometryLibrary(); /// Writes the given mesh void WriteGeometry( size_t pIndex); - enum FloatDataType { FloatType_Vector, FloatType_TexCoord2, FloatType_TexCoord3, FloatType_Color }; + enum FloatDataType { FloatType_Vector, FloatType_TexCoord2, FloatType_TexCoord3, FloatType_Color, FloatType_Mat4x4, FloatType_Weight }; /// Writes a float array of the given type void WriteFloatArray( const std::string& pIdString, FloatDataType pType, const ai_real* pData, size_t pElementCount); diff --git a/code/ImporterRegistry.cpp b/code/ImporterRegistry.cpp index 7b965c26a..5d04b267e 100644 --- a/code/ImporterRegistry.cpp +++ b/code/ImporterRegistry.cpp @@ -192,6 +192,9 @@ corresponding preprocessor flag to selectively disable formats. #ifndef ASSIMP_BUILD_NO_X3D_IMPORTER # include "X3DImporter.hpp" #endif +#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER +# include "MMDImporter.h" +#endif namespace Assimp { @@ -336,11 +339,14 @@ void GetImporterInstanceList(std::vector< BaseImporter* >& out) out.push_back( new C4DImporter() ); #endif #if ( !defined ASSIMP_BUILD_NO_3MF_IMPORTER ) - out.push_back(new D3MFImporter() ); + out.push_back( new D3MFImporter() ); #endif #ifndef ASSIMP_BUILD_NO_X3D_IMPORTER out.push_back( new X3DImporter() ); #endif +#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER + out.push_back( new MMDImporter() ); +#endif } /** will delete all registered importers. */ diff --git a/code/MMDCpp14.h b/code/MMDCpp14.h new file mode 100644 index 000000000..ad112de0f --- /dev/null +++ b/code/MMDCpp14.h @@ -0,0 +1,42 @@ +#pragma once + +#ifndef MMD_CPP14_H +#define MMD_CPP14_H + +#include +#include +#include +#include + +namespace mmd { + template struct _Unique_if { + typedef std::unique_ptr _Single_object; + }; + + template struct _Unique_if { + typedef std::unique_ptr _Unknown_bound; + }; + + template struct _Unique_if { + typedef void _Known_bound; + }; + + template + typename _Unique_if::_Single_object + make_unique(Args&&... args) { + return std::unique_ptr(new T(std::forward(args)...)); + } + + template + typename _Unique_if::_Unknown_bound + make_unique(size_t n) { + typedef typename std::remove_extent::type U; + return std::unique_ptr(new U[n]()); + } + + template + typename _Unique_if::_Known_bound + make_unique(Args&&...) = delete; +} + +#endif diff --git a/code/MMDImporter.cpp b/code/MMDImporter.cpp new file mode 100644 index 000000000..149761c34 --- /dev/null +++ b/code/MMDImporter.cpp @@ -0,0 +1,370 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2016, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER + +#include "MMDImporter.h" +#include "MMDPmdParser.h" +#include "MMDPmxParser.h" +#include "MMDVmdParser.h" +#include "ConvertToLHProcess.h" +#include +#include +#include +#include +#include +#include +#include + +static const aiImporterDesc desc = {"MMD Importer", + "", + "", + "surfaces supported?", + aiImporterFlags_SupportTextFlavour, + 0, + 0, + 0, + 0, + "pmx"}; + +namespace Assimp { + +using namespace std; + +// ------------------------------------------------------------------------------------------------ +// Default constructor +MMDImporter::MMDImporter() + : m_Buffer(), + // m_pRootObject( NULL ), + m_strAbsPath("") { + DefaultIOSystem io; + m_strAbsPath = io.getOsSeparator(); +} + +// ------------------------------------------------------------------------------------------------ +// Destructor. +MMDImporter::~MMDImporter() { + // delete m_pRootObject; + // m_pRootObject = NULL; +} + +// ------------------------------------------------------------------------------------------------ +// Returns true, if file is an pmx file. +bool MMDImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, + bool checkSig) const { + if (!checkSig) // Check File Extension + { + return SimpleExtensionCheck(pFile, "pmx"); + } else // Check file Header + { + static const char *pTokens[] = {"PMX "}; + return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, pTokens, + 1); + } +} + +// ------------------------------------------------------------------------------------------------ +const aiImporterDesc *MMDImporter::GetInfo() const { return &desc; } + +// ------------------------------------------------------------------------------------------------ +// MMD import implementation +void MMDImporter::InternReadFile(const std::string &file, aiScene *pScene, + IOSystem *pIOHandler) { + // Read file by istream + std::filebuf fb; + if (!fb.open(file, std::ios::in | std::ios::binary)) { + throw DeadlyImportError("Failed to open file " + file + "."); + } + + std::istream fileStream(&fb); + + // Get the file-size and validate it, throwing an exception when fails + 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")); + + // split mesh by materials + 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; + } + + // create node hierarchy for bone position + 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); + } + } + + // create materials + 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); + } + + // Convert everything to OpenGL space + 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; // trianglular face + 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; + + // additional UVs + for (int i = 1; i <= pModel->setting.uv; i++) { + pMesh->mTextureCoords[i] = new aiVector3D[pMesh->mNumVertices]; + pMesh->mNumUVComponents[i] = 4; + } + + map> bone_vertex_map; + + // fill in contents and create bones + 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++) { + // TODO: wrong here? use quaternion transform? + pMesh->mTextureCoords[i][index].x = v->uva[i][0]; + pMesh->mTextureCoords[i][index].y = v->uva[i][1]; + } + + // handle bone map + const auto vsBDEF1_ptr = + dynamic_cast(v->skinning.get()); + const auto vsBDEF2_ptr = + dynamic_cast(v->skinning.get()); + const auto vsBDEF4_ptr = + dynamic_cast(v->skinning.get()); + const auto vsSDEF_ptr = + dynamic_cast(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: // TODO: how to use sdef_c, sdef_r0, + // sdef_r1? + 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(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; + } + } + + // make all bones for each mesh + // assign bone weights to skinned bones (otherwise just initialize) + 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)); + } + 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; +} + +// ------------------------------------------------------------------------------------------------ + +} // Namespace Assimp + +#endif // !! ASSIMP_BUILD_NO_MMD_IMPORTER diff --git a/code/MMDImporter.h b/code/MMDImporter.h new file mode 100644 index 000000000..e660abaaa --- /dev/null +++ b/code/MMDImporter.h @@ -0,0 +1,96 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2016, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#ifndef MMD_FILE_IMPORTER_H_INC +#define MMD_FILE_IMPORTER_H_INC + +#include "BaseImporter.h" +#include "MMDPmxParser.h" +#include +#include + +struct aiMesh; + +namespace Assimp { + +// ------------------------------------------------------------------------------------------------ +/// \class MMDImporter +/// \brief Imports MMD a pmx/pmd/vmd file +// ------------------------------------------------------------------------------------------------ +class MMDImporter : public BaseImporter { +public: + /// \brief Default constructor + MMDImporter(); + + /// \brief Destructor + ~MMDImporter(); + +public: + /// \brief Returns whether the class can handle the format of the given file. + /// \remark See BaseImporter::CanRead() for details. + bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const; + +private: + //! \brief Appends the supported extension. + const aiImporterDesc* GetInfo () const; + + //! \brief File import implementation. + void InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); + + //! \brief Create the data from imported content. + void CreateDataFromImport(const pmx::PmxModel* pModel, aiScene* pScene); + + //! \brief Create the mesh + aiMesh* CreateMesh(const pmx::PmxModel* pModel, const int indexStart, const int indexCount); + + //! \brief Create the material + aiMaterial* CreateMaterial(const pmx::PmxMaterial* pMat, const pmx::PmxModel* pModel); + +private: + //! Data buffer + std::vector m_Buffer; + //! Absolute pathname of model in file system + std::string m_strAbsPath; +}; + +// ------------------------------------------------------------------------------------------------ + +} // Namespace Assimp + +#endif \ No newline at end of file diff --git a/code/MMDPmdParser.h b/code/MMDPmdParser.h new file mode 100644 index 000000000..375c323df --- /dev/null +++ b/code/MMDPmdParser.h @@ -0,0 +1,630 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "MMDCpp14.h" + +namespace pmd +{ + /// ヘッダ + class PmdHeader + { + public: + /// モデル名 + std::string name; + /// モデル名(英語) + std::string name_english; + /// コメント + std::string comment; + /// コメント(英語) + std::string comment_english; + + bool Read(std::ifstream* stream) + { + char buffer[256]; + stream->read(buffer, 20); + name = std::string(buffer); + stream->read(buffer, 256); + comment = std::string(buffer); + return true; + } + + bool ReadExtension(std::ifstream* stream) + { + char buffer[256]; + stream->read(buffer, 20); + name_english = std::string(buffer); + stream->read(buffer, 256); + comment_english = std::string(buffer); + return true; + } + }; + + /// 頂点 + class PmdVertex + { + public: + /// 位置 + float position[3]; + + /// 法線 + float normal[3]; + + /// UV座標 + float uv[2]; + + /// 関連ボーンインデックス + uint16_t bone_index[2]; + + /// ボーンウェイト + uint8_t bone_weight; + + /// エッジ不可視 + bool edge_invisible; + + bool Read(std::ifstream* stream) + { + stream->read((char*) position, sizeof(float) * 3); + stream->read((char*) normal, sizeof(float) * 3); + stream->read((char*) uv, sizeof(float) * 2); + stream->read((char*) bone_index, sizeof(uint16_t) * 2); + stream->read((char*) &bone_weight, sizeof(uint8_t)); + stream->read((char*) &edge_invisible, sizeof(uint8_t)); + return true; + } + }; + + /// 材質 + class PmdMaterial + { + public: + /// 減衰色 + float diffuse[4]; + /// 光沢度 + float power; + /// 光沢色 + float specular[3]; + /// 環境色 + float ambient[3]; + /// トーンインデックス + uint8_t toon_index; + /// エッジ + uint8_t edge_flag; + /// インデックス数 + uint32_t index_count; + /// テクスチャファイル名 + std::string texture_filename; + /// スフィアファイル名 + std::string sphere_filename; + + bool Read(std::ifstream* stream) + { + char buffer[20]; + stream->read((char*) &diffuse, sizeof(float) * 4); + stream->read((char*) &power, sizeof(float)); + stream->read((char*) &specular, sizeof(float) * 3); + stream->read((char*) &ambient, sizeof(float) * 3); + stream->read((char*) &toon_index, sizeof(uint8_t)); + stream->read((char*) &edge_flag, sizeof(uint8_t)); + stream->read((char*) &index_count, sizeof(uint32_t)); + stream->read((char*) &buffer, sizeof(char) * 20); + char* pstar = strchr(buffer, '*'); + if (NULL == pstar) + { + texture_filename = std::string(buffer); + sphere_filename.clear(); + } + else { + *pstar = (char)NULL; + texture_filename = std::string(buffer); + sphere_filename = std::string(pstar+1); + } + return true; + } + }; + + enum class BoneType : uint8_t + { + Rotation, + RotationAndMove, + IkEffector, + Unknown, + IkEffectable, + RotationEffectable, + IkTarget, + Invisible, + Twist, + RotationMovement + }; + + /// ボーン + class PmdBone + { + public: + /// ボーン名 + std::string name; + /// ボーン名(英語) + std::string name_english; + /// 親ボーン番号 + uint16_t parent_bone_index; + /// 末端ボーン番号 + uint16_t tail_pos_bone_index; + /// ボーン種類 + BoneType bone_type; + /// IKボーン番号 + uint16_t ik_parent_bone_index; + /// ボーンのヘッドの位置 + float bone_head_pos[3]; + + void Read(std::istream *stream) + { + char buffer[20]; + stream->read(buffer, 20); + name = std::string(buffer); + stream->read((char*) &parent_bone_index, sizeof(uint16_t)); + stream->read((char*) &tail_pos_bone_index, sizeof(uint16_t)); + stream->read((char*) &bone_type, sizeof(uint8_t)); + stream->read((char*) &ik_parent_bone_index, sizeof(uint16_t)); + stream->read((char*) &bone_head_pos, sizeof(float) * 3); + } + + void ReadExpantion(std::istream *stream) + { + char buffer[20]; + stream->read(buffer, 20); + name_english = std::string(buffer); + } + }; + + /// IK + class PmdIk + { + public: + /// IKボーン番号 + uint16_t ik_bone_index; + /// IKターゲットボーン番号 + uint16_t target_bone_index; + /// 再帰回数 + uint16_t interations; + /// 角度制限 + float angle_limit; + /// 影響下ボーン番号 + std::vector ik_child_bone_index; + + void Read(std::istream *stream) + { + stream->read((char *) &ik_bone_index, sizeof(uint16_t)); + stream->read((char *) &target_bone_index, sizeof(uint16_t)); + uint8_t ik_chain_length; + stream->read((char*) &ik_chain_length, sizeof(uint8_t)); + stream->read((char *) &interations, sizeof(uint16_t)); + stream->read((char *) &angle_limit, sizeof(float)); + ik_child_bone_index.resize(ik_chain_length); + for (int i = 0; i < ik_chain_length; i++) + { + stream->read((char *) &ik_child_bone_index[i], sizeof(uint16_t)); + } + } + }; + + class PmdFaceVertex + { + public: + int vertex_index; + float position[3]; + + void Read(std::istream *stream) + { + stream->read((char *) &vertex_index, sizeof(int)); + stream->read((char *) position, sizeof(float) * 3); + } + }; + + enum class FaceCategory : uint8_t + { + Base, + Eyebrow, + Eye, + Mouth, + Other + }; + + class PmdFace + { + public: + std::string name; + FaceCategory type; + std::vector vertices; + std::string name_english; + + void Read(std::istream *stream) + { + char buffer[20]; + stream->read(buffer, 20); + name = std::string(buffer); + int vertex_count; + stream->read((char*) &vertex_count, sizeof(int)); + stream->read((char*) &type, sizeof(uint8_t)); + vertices.resize(vertex_count); + for (int i = 0; i < vertex_count; i++) + { + vertices[i].Read(stream); + } + } + + void ReadExpantion(std::istream *stream) + { + char buffer[20]; + stream->read(buffer, 20); + name_english = std::string(buffer); + } + }; + + /// ボーン枠用の枠名 + class PmdBoneDispName + { + public: + std::string bone_disp_name; + std::string bone_disp_name_english; + + void Read(std::istream *stream) + { + char buffer[50]; + stream->read(buffer, 50); + bone_disp_name = std::string(buffer); + bone_disp_name_english.clear(); + } + void ReadExpantion(std::istream *stream) + { + char buffer[50]; + stream->read(buffer, 50); + bone_disp_name_english = std::string(buffer); + } + }; + + class PmdBoneDisp + { + public: + uint16_t bone_index; + uint8_t bone_disp_index; + + void Read(std::istream *stream) + { + stream->read((char*) &bone_index, sizeof(uint16_t)); + stream->read((char*) &bone_disp_index, sizeof(uint8_t)); + } + }; + + /// 衝突形状 + enum class RigidBodyShape : uint8_t + { + /// 球 + Sphere = 0, + /// 直方体 + Box = 1, + /// カプセル + Cpusel = 2 + }; + + /// 剛体タイプ + enum class RigidBodyType : uint8_t + { + /// ボーン追従 + BoneConnected = 0, + /// 物理演算 + Physics = 1, + /// 物理演算(Bone位置合せ) + ConnectedPhysics = 2 + }; + + /// 剛体 + class PmdRigidBody + { + public: + /// 名前 + std::string name; + /// 関連ボーン番号 + uint16_t related_bone_index; + /// グループ番号 + uint8_t group_index; + /// マスク + uint16_t mask; + /// 形状 + RigidBodyShape shape; + /// 大きさ + float size[3]; + /// 位置 + float position[3]; + /// 回転 + float orientation[3]; + /// 質量 + float weight; + /// 移動ダンピング + float linear_damping; + /// 回転ダンピング + float anglar_damping; + /// 反発係数 + float restitution; + /// 摩擦係数 + float friction; + /// 演算方法 + RigidBodyType rigid_type; + + void Read(std::istream *stream) + { + char buffer[20]; + stream->read(buffer, sizeof(char) * 20); + name = (std::string(buffer)); + stream->read((char*) &related_bone_index, sizeof(uint16_t)); + stream->read((char*) &group_index, sizeof(uint8_t)); + stream->read((char*) &mask, sizeof(uint16_t)); + stream->read((char*) &shape, sizeof(uint8_t)); + stream->read((char*) size, sizeof(float) * 3); + stream->read((char*) position, sizeof(float) * 3); + stream->read((char*) orientation, sizeof(float) * 3); + stream->read((char*) &weight, sizeof(float)); + stream->read((char*) &linear_damping, sizeof(float)); + stream->read((char*) &anglar_damping, sizeof(float)); + stream->read((char*) &restitution, sizeof(float)); + stream->read((char*) &friction, sizeof(float)); + stream->read((char*) &rigid_type, sizeof(char)); + } + }; + + /// 剛体の拘束 + class PmdConstraint + { + public: + /// 名前 + std::string name; + /// 剛体Aのインデックス + uint32_t rigid_body_index_a; + /// 剛体Bのインデックス + uint32_t rigid_body_index_b; + /// 位置 + float position[3]; + /// 回転 + float orientation[3]; + /// 最小移動制限 + float linear_lower_limit[3]; + /// 最大移動制限 + float linear_upper_limit[3]; + /// 最小回転制限 + float angular_lower_limit[3]; + /// 最大回転制限 + float angular_upper_limit[3]; + /// 移動に対する復元力 + float linear_stiffness[3]; + /// 回転に対する復元力 + float angular_stiffness[3]; + + void Read(std::istream *stream) + { + char buffer[20]; + stream->read(buffer, 20); + name = std::string(buffer); + stream->read((char *) &rigid_body_index_a, sizeof(uint32_t)); + stream->read((char *) &rigid_body_index_b, sizeof(uint32_t)); + stream->read((char *) position, sizeof(float) * 3); + stream->read((char *) orientation, sizeof(float) * 3); + stream->read((char *) linear_lower_limit, sizeof(float) * 3); + stream->read((char *) linear_upper_limit, sizeof(float) * 3); + stream->read((char *) angular_lower_limit, sizeof(float) * 3); + stream->read((char *) angular_upper_limit, sizeof(float) * 3); + stream->read((char *) linear_stiffness, sizeof(float) * 3); + stream->read((char *) angular_stiffness, sizeof(float) * 3); + } + }; + + /// PMDモデル + class PmdModel + { + public: + float version; + PmdHeader header; + std::vector vertices; + std::vector indices; + std::vector materials; + std::vector bones; + std::vector iks; + std::vector faces; + std::vector faces_indices; + std::vector bone_disp_name; + std::vector bone_disp; + std::vector toon_filenames; + std::vector rigid_bodies; + std::vector constraints; + + static std::unique_ptr LoadFromFile(const char *filename) + { + std::ifstream stream(filename, std::ios::binary); + if (stream.fail()) + { + std::cerr << "could not open \"" << filename << "\"" << std::endl; + return nullptr; + } + auto result = LoadFromStream(&stream); + stream.close(); + return result; + } + + /// ファイルからPmdModelを生成する + static std::unique_ptr LoadFromStream(std::ifstream *stream) + { + auto result = mmd::make_unique(); + char buffer[100]; + + // magic + char magic[3]; + stream->read(magic, 3); + if (magic[0] != 'P' || magic[1] != 'm' || magic[2] != 'd') + { + std::cerr << "invalid file" << std::endl; + return nullptr; + } + + // version + stream->read((char*) &(result->version), sizeof(float)); + if (result ->version != 1.0f) + { + std::cerr << "invalid version" << std::endl; + return nullptr; + } + + // header + result->header.Read(stream); + + // vertices + uint32_t vertex_num; + stream->read((char*) &vertex_num, sizeof(uint32_t)); + result->vertices.resize(vertex_num); + for (uint32_t i = 0; i < vertex_num; i++) + { + result->vertices[i].Read(stream); + } + + // indices + uint32_t index_num; + stream->read((char*) &index_num, sizeof(uint32_t)); + result->indices.resize(index_num); + for (uint32_t i = 0; i < index_num; i++) + { + stream->read((char*) &result->indices[i], sizeof(uint16_t)); + } + + // materials + uint32_t material_num; + stream->read((char*) &material_num, sizeof(uint32_t)); + result->materials.resize(material_num); + for (uint32_t i = 0; i < material_num; i++) + { + result->materials[i].Read(stream); + } + + // bones + uint16_t bone_num; + stream->read((char*) &bone_num, sizeof(uint16_t)); + result->bones.resize(bone_num); + for (uint32_t i = 0; i < bone_num; i++) + { + result->bones[i].Read(stream); + } + + // iks + uint16_t ik_num; + stream->read((char*) &ik_num, sizeof(uint16_t)); + result->iks.resize(ik_num); + for (uint32_t i = 0; i < ik_num; i++) + { + result->iks[i].Read(stream); + } + + // faces + uint16_t face_num; + stream->read((char*) &face_num, sizeof(uint16_t)); + result->faces.resize(face_num); + for (uint32_t i = 0; i < face_num; i++) + { + result->faces[i].Read(stream); + } + + // face frames + uint8_t face_frame_num; + stream->read((char*) &face_frame_num, sizeof(uint8_t)); + result->faces_indices.resize(face_frame_num); + for (uint32_t i = 0; i < face_frame_num; i++) + { + stream->read((char*) &result->faces_indices[i], sizeof(uint16_t)); + } + + // bone names + uint8_t bone_disp_num; + stream->read((char*) &bone_disp_num, sizeof(uint8_t)); + result->bone_disp_name.resize(bone_disp_num); + for (uint32_t i = 0; i < bone_disp_num; i++) + { + result->bone_disp_name[i].Read(stream); + } + + // bone frame + uint32_t bone_frame_num; + stream->read((char*) &bone_frame_num, sizeof(uint32_t)); + result->bone_disp.resize(bone_frame_num); + for (uint32_t i = 0; i < bone_frame_num; i++) + { + result->bone_disp[i].Read(stream); + } + + // english name + bool english; + stream->read((char*) &english, sizeof(char)); + if (english) + { + result->header.ReadExtension(stream); + for (uint32_t i = 0; i < bone_num; i++) + { + result->bones[i].ReadExpantion(stream); + } + for (uint32_t i = 0; i < face_num; i++) + { + if (result->faces[i].type == pmd::FaceCategory::Base) + { + continue; + } + result->faces[i].ReadExpantion(stream); + } + for (uint32_t i = 0; i < result->bone_disp_name.size(); i++) + { + result->bone_disp_name[i].ReadExpantion(stream); + } + } + + // toon textures + if (stream->peek() == std::ios::traits_type::eof()) + { + result->toon_filenames.clear(); + } + else { + result->toon_filenames.resize(10); + for (uint32_t i = 0; i < 10; i++) + { + stream->read(buffer, 100); + result->toon_filenames[i] = std::string(buffer); + } + } + + // physics + if (stream->peek() == std::ios::traits_type::eof()) + { + result->rigid_bodies.clear(); + result->constraints.clear(); + } + else { + uint32_t rigid_body_num; + stream->read((char*) &rigid_body_num, sizeof(uint32_t)); + result->rigid_bodies.resize(rigid_body_num); + for (uint32_t i = 0; i < rigid_body_num; i++) + { + result->rigid_bodies[i].Read(stream); + } + uint32_t constraint_num; + stream->read((char*) &constraint_num, sizeof(uint32_t)); + result->constraints.resize(constraint_num); + for (uint32_t i = 0; i < constraint_num; i++) + { + result->constraints[i].Read(stream); + } + } + + if (stream->peek() != std::ios::traits_type::eof()) + { + std::cerr << "there is unknown data" << std::endl; + } + + return result; + } + }; +} diff --git a/code/MMDPmxParser.cpp b/code/MMDPmxParser.cpp new file mode 100644 index 000000000..352259df5 --- /dev/null +++ b/code/MMDPmxParser.cpp @@ -0,0 +1,608 @@ +#include +#include "MMDPmxParser.h" +#include "Exceptional.h" +#include "../contrib/ConvertUTF/ConvertUTF.h" + +namespace pmx +{ + /// インデックス値を読み込む + int ReadIndex(std::istream *stream, int size) + { + switch (size) + { + case 1: + uint8_t tmp8; + stream->read((char*) &tmp8, sizeof(uint8_t)); + if (255 == tmp8) + { + return -1; + } + else { + return (int) tmp8; + } + case 2: + uint16_t tmp16; + stream->read((char*) &tmp16, sizeof(uint16_t)); + if (65535 == tmp16) + { + return -1; + } + else { + return (int) tmp16; + } + case 4: + int tmp32; + stream->read((char*) &tmp32, sizeof(int)); + return tmp32; + default: + return -1; + } + } + + /// 文字列を読み込む + std::string ReadString(std::istream *stream, uint8_t encoding) + { + int size; + stream->read((char*) &size, sizeof(int)); + std::vector buffer; + if (size == 0) + { + return std::string(""); + } + buffer.reserve(size); + stream->read((char*) buffer.data(), size); + if (encoding == 0) + { + // UTF16 to UTF8 + std::string result; + + const char* sourceStart = buffer.data(); + const unsigned int targetSize = size * 3; // enough to encode + char* targetStart = new char[targetSize](); + const char* targetReserved = targetStart; + ConversionFlags flags = ConversionFlags::lenientConversion; + + ConversionResult conversionResult; + if( ( conversionResult = ConvertUTF16toUTF8( + (const UTF16**)&sourceStart, (const UTF16*)(sourceStart + size), + (UTF8**)&targetStart, (UTF8*)(targetStart + targetSize), + flags) ) != ConversionResult::conversionOK) { + throw DeadlyImportError( "Convert " + std::string(sourceStart) + " to UTF8 is not valid." ); + } + + result.assign(targetReserved, targetStart - targetReserved); + delete[] targetReserved; + return result; + } + else + { + // the name is already UTF8 + return std::string((const char*)buffer.data(), size); + } + } + + void PmxSetting::Read(std::istream *stream) + { + uint8_t count; + stream->read((char*) &count, sizeof(uint8_t)); + if (count < 8) + { + throw; + } + stream->read((char*) &encoding, sizeof(uint8_t)); + stream->read((char*) &uv, sizeof(uint8_t)); + stream->read((char*) &vertex_index_size, sizeof(uint8_t)); + stream->read((char*) &texture_index_size, sizeof(uint8_t)); + stream->read((char*) &material_index_size, sizeof(uint8_t)); + stream->read((char*) &bone_index_size, sizeof(uint8_t)); + stream->read((char*) &morph_index_size, sizeof(uint8_t)); + stream->read((char*) &rigidbody_index_size, sizeof(uint8_t)); + uint8_t temp; + for (int i = 8; i < count; i++) + { + stream->read((char*)&temp, sizeof(uint8_t)); + } + } + + void PmxVertexSkinningBDEF1::Read(std::istream *stream, PmxSetting *setting) + { + this->bone_index = ReadIndex(stream, setting->bone_index_size); + } + + void PmxVertexSkinningBDEF2::Read(std::istream *stream, PmxSetting *setting) + { + this->bone_index1 = ReadIndex(stream, setting->bone_index_size); + this->bone_index2 = ReadIndex(stream, setting->bone_index_size); + stream->read((char*) &this->bone_weight, sizeof(float)); + } + + void PmxVertexSkinningBDEF4::Read(std::istream *stream, PmxSetting *setting) + { + this->bone_index1 = ReadIndex(stream, setting->bone_index_size); + this->bone_index2 = ReadIndex(stream, setting->bone_index_size); + this->bone_index3 = ReadIndex(stream, setting->bone_index_size); + this->bone_index4 = ReadIndex(stream, setting->bone_index_size); + stream->read((char*) &this->bone_weight1, sizeof(float)); + stream->read((char*) &this->bone_weight2, sizeof(float)); + stream->read((char*) &this->bone_weight3, sizeof(float)); + stream->read((char*) &this->bone_weight4, sizeof(float)); + } + + void PmxVertexSkinningSDEF::Read(std::istream *stream, PmxSetting *setting) + { + this->bone_index1 = ReadIndex(stream, setting->bone_index_size); + this->bone_index2 = ReadIndex(stream, setting->bone_index_size); + stream->read((char*) &this->bone_weight, sizeof(float)); + stream->read((char*) this->sdef_c, sizeof(float) * 3); + stream->read((char*) this->sdef_r0, sizeof(float) * 3); + stream->read((char*) this->sdef_r1, sizeof(float) * 3); + } + + void PmxVertexSkinningQDEF::Read(std::istream *stream, PmxSetting *setting) + { + this->bone_index1 = ReadIndex(stream, setting->bone_index_size); + this->bone_index2 = ReadIndex(stream, setting->bone_index_size); + this->bone_index3 = ReadIndex(stream, setting->bone_index_size); + this->bone_index4 = ReadIndex(stream, setting->bone_index_size); + stream->read((char*) &this->bone_weight1, sizeof(float)); + stream->read((char*) &this->bone_weight2, sizeof(float)); + stream->read((char*) &this->bone_weight3, sizeof(float)); + stream->read((char*) &this->bone_weight4, sizeof(float)); + } + + void PmxVertex::Read(std::istream *stream, PmxSetting *setting) + { + stream->read((char*) this->position, sizeof(float) * 3); + stream->read((char*) this->normal, sizeof(float) * 3); + stream->read((char*) this->uv, sizeof(float) * 2); + for (int i = 0; i < setting->uv; ++i) + { + stream->read((char*) this->uva[i], sizeof(float) * 4); + } + stream->read((char*) &this->skinning_type, sizeof(PmxVertexSkinningType)); + switch (this->skinning_type) + { + case PmxVertexSkinningType::BDEF1: + this->skinning = mmd::make_unique(); + break; + case PmxVertexSkinningType::BDEF2: + this->skinning = mmd::make_unique(); + break; + case PmxVertexSkinningType::BDEF4: + this->skinning = mmd::make_unique(); + break; + case PmxVertexSkinningType::SDEF: + this->skinning = mmd::make_unique(); + break; + case PmxVertexSkinningType::QDEF: + this->skinning = mmd::make_unique(); + break; + default: + throw "invalid skinning type"; + } + this->skinning->Read(stream, setting); + stream->read((char*) &this->edge, sizeof(float)); + } + + void PmxMaterial::Read(std::istream *stream, PmxSetting *setting) + { + this->material_name = std::move(ReadString(stream, setting->encoding)); + this->material_english_name = std::move(ReadString(stream, setting->encoding)); + stream->read((char*) this->diffuse, sizeof(float) * 4); + stream->read((char*) this->specular, sizeof(float) * 3); + stream->read((char*) &this->specularlity, sizeof(float)); + stream->read((char*) this->ambient, sizeof(float) * 3); + stream->read((char*) &this->flag, sizeof(uint8_t)); + stream->read((char*) this->edge_color, sizeof(float) * 4); + stream->read((char*) &this->edge_size, sizeof(float)); + this->diffuse_texture_index = ReadIndex(stream, setting->texture_index_size); + this->sphere_texture_index = ReadIndex(stream, setting->texture_index_size); + stream->read((char*) &this->sphere_op_mode, sizeof(uint8_t)); + stream->read((char*) &this->common_toon_flag, sizeof(uint8_t)); + if (this->common_toon_flag) + { + stream->read((char*) &this->toon_texture_index, sizeof(uint8_t)); + } + else { + this->toon_texture_index = ReadIndex(stream, setting->texture_index_size); + } + this->memo = std::move(ReadString(stream, setting->encoding)); + stream->read((char*) &this->index_count, sizeof(int)); + } + + void PmxIkLink::Read(std::istream *stream, PmxSetting *setting) + { + this->link_target = ReadIndex(stream, setting->bone_index_size); + stream->read((char*) &this->angle_lock, sizeof(uint8_t)); + if (angle_lock == 1) + { + stream->read((char*) this->max_radian, sizeof(float) * 3); + stream->read((char*) this->min_radian, sizeof(float) * 3); + } + } + + void PmxBone::Read(std::istream *stream, PmxSetting *setting) + { + this->bone_name = std::move(ReadString(stream, setting->encoding)); + this->bone_english_name = std::move(ReadString(stream, setting->encoding)); + stream->read((char*) this->position, sizeof(float) * 3); + this->parent_index = ReadIndex(stream, setting->bone_index_size); + stream->read((char*) &this->level, sizeof(int)); + stream->read((char*) &this->bone_flag, sizeof(uint16_t)); + if (this->bone_flag & 0x0001) { + this->target_index = ReadIndex(stream, setting->bone_index_size); + } + else { + stream->read((char*)this->offset, sizeof(float) * 3); + } + if (this->bone_flag & (0x0100 | 0x0200)) { + this->grant_parent_index = ReadIndex(stream, setting->bone_index_size); + stream->read((char*) &this->grant_weight, sizeof(float)); + } + if (this->bone_flag & 0x0400) { + stream->read((char*)this->lock_axis_orientation, sizeof(float) * 3); + } + if (this->bone_flag & 0x0800) { + stream->read((char*)this->local_axis_x_orientation, sizeof(float) * 3); + stream->read((char*)this->local_axis_y_orientation, sizeof(float) * 3); + } + if (this->bone_flag & 0x2000) { + stream->read((char*) &this->key, sizeof(int)); + } + if (this->bone_flag & 0x0020) { + this->ik_target_bone_index = ReadIndex(stream, setting->bone_index_size); + stream->read((char*) &ik_loop, sizeof(int)); + stream->read((char*) &ik_loop_angle_limit, sizeof(float)); + stream->read((char*) &ik_link_count, sizeof(int)); + this->ik_links = mmd::make_unique(ik_link_count); + for (int i = 0; i < ik_link_count; i++) { + ik_links[i].Read(stream, setting); + } + } + } + + void PmxMorphVertexOffset::Read(std::istream *stream, PmxSetting *setting) + { + this->vertex_index = ReadIndex(stream, setting->vertex_index_size); + stream->read((char*)this->position_offset, sizeof(float) * 3); + } + + void PmxMorphUVOffset::Read(std::istream *stream, PmxSetting *setting) + { + this->vertex_index = ReadIndex(stream, setting->vertex_index_size); + stream->read((char*)this->uv_offset, sizeof(float) * 4); + } + + void PmxMorphBoneOffset::Read(std::istream *stream, PmxSetting *setting) + { + this->bone_index = ReadIndex(stream, setting->bone_index_size); + stream->read((char*)this->translation, sizeof(float) * 3); + stream->read((char*)this->rotation, sizeof(float) * 4); + } + + void PmxMorphMaterialOffset::Read(std::istream *stream, PmxSetting *setting) + { + this->material_index = ReadIndex(stream, setting->material_index_size); + stream->read((char*) &this->offset_operation, sizeof(uint8_t)); + stream->read((char*)this->diffuse, sizeof(float) * 4); + stream->read((char*)this->specular, sizeof(float) * 3); + stream->read((char*) &this->specularity, sizeof(float)); + stream->read((char*)this->ambient, sizeof(float) * 3); + stream->read((char*)this->edge_color, sizeof(float) * 4); + stream->read((char*) &this->edge_size, sizeof(float)); + stream->read((char*)this->texture_argb, sizeof(float) * 4); + stream->read((char*)this->sphere_texture_argb, sizeof(float) * 4); + stream->read((char*)this->toon_texture_argb, sizeof(float) * 4); + } + + void PmxMorphGroupOffset::Read(std::istream *stream, PmxSetting *setting) + { + this->morph_index = ReadIndex(stream, setting->morph_index_size); + stream->read((char*) &this->morph_weight, sizeof(float)); + } + + void PmxMorphFlipOffset::Read(std::istream *stream, PmxSetting *setting) + { + this->morph_index = ReadIndex(stream, setting->morph_index_size); + stream->read((char*) &this->morph_value, sizeof(float)); + } + + void PmxMorphImplusOffset::Read(std::istream *stream, PmxSetting *setting) + { + this->rigid_body_index = ReadIndex(stream, setting->rigidbody_index_size); + stream->read((char*) &this->is_local, sizeof(uint8_t)); + stream->read((char*)this->velocity, sizeof(float) * 3); + stream->read((char*)this->angular_torque, sizeof(float) * 3); + } + + void PmxMorph::Read(std::istream *stream, PmxSetting *setting) + { + this->morph_name = ReadString(stream, setting->encoding); + this->morph_english_name = ReadString(stream, setting->encoding); + stream->read((char*) &category, sizeof(MorphCategory)); + stream->read((char*) &morph_type, sizeof(MorphType)); + stream->read((char*) &this->offset_count, sizeof(int)); + switch (this->morph_type) + { + case MorphType::Group: + group_offsets = mmd::make_unique(this->offset_count); + for (int i = 0; i < offset_count; i++) + { + group_offsets[i].Read(stream, setting); + } + break; + case MorphType::Vertex: + vertex_offsets = mmd::make_unique(this->offset_count); + for (int i = 0; i < offset_count; i++) + { + vertex_offsets[i].Read(stream, setting); + } + break; + case MorphType::Bone: + bone_offsets = mmd::make_unique(this->offset_count); + for (int i = 0; i < offset_count; i++) + { + bone_offsets[i].Read(stream, setting); + } + break; + case MorphType::Matrial: + material_offsets = mmd::make_unique(this->offset_count); + for (int i = 0; i < offset_count; i++) + { + material_offsets[i].Read(stream, setting); + } + break; + case MorphType::UV: + case MorphType::AdditionalUV1: + case MorphType::AdditionalUV2: + case MorphType::AdditionalUV3: + case MorphType::AdditionalUV4: + uv_offsets = mmd::make_unique(this->offset_count); + for (int i = 0; i < offset_count; i++) + { + uv_offsets[i].Read(stream, setting); + } + break; + default: + throw; + } + } + + void PmxFrameElement::Read(std::istream *stream, PmxSetting *setting) + { + stream->read((char*) &this->element_target, sizeof(uint8_t)); + if (this->element_target == 0x00) + { + this->index = ReadIndex(stream, setting->bone_index_size); + } + else { + this->index = ReadIndex(stream, setting->morph_index_size); + } + } + + void PmxFrame::Read(std::istream *stream, PmxSetting *setting) + { + this->frame_name = ReadString(stream, setting->encoding); + this->frame_english_name = ReadString(stream, setting->encoding); + stream->read((char*) &this->frame_flag, sizeof(uint8_t)); + stream->read((char*) &this->element_count, sizeof(int)); + this->elements = mmd::make_unique(this->element_count); + for (int i = 0; i < this->element_count; i++) + { + this->elements[i].Read(stream, setting); + } + } + + void PmxRigidBody::Read(std::istream *stream, PmxSetting *setting) + { + this->girid_body_name = ReadString(stream, setting->encoding); + this->girid_body_english_name = ReadString(stream, setting->encoding); + this->target_bone = ReadIndex(stream, setting->bone_index_size); + stream->read((char*) &this->group, sizeof(uint8_t)); + stream->read((char*) &this->mask, sizeof(uint16_t)); + stream->read((char*) &this->shape, sizeof(uint8_t)); + stream->read((char*) this->size, sizeof(float) * 3); + stream->read((char*) this->position, sizeof(float) * 3); + stream->read((char*) this->orientation, sizeof(float) * 3); + stream->read((char*) &this->mass, sizeof(float)); + stream->read((char*) &this->move_attenuation, sizeof(float)); + stream->read((char*) &this->rotation_attenuation, sizeof(float)); + stream->read((char*) &this->repulsion, sizeof(float)); + stream->read((char*) &this->friction, sizeof(float)); + stream->read((char*) &this->physics_calc_type, sizeof(uint8_t)); + } + + void PmxJointParam::Read(std::istream *stream, PmxSetting *setting) + { + this->rigid_body1 = ReadIndex(stream, setting->rigidbody_index_size); + this->rigid_body2 = ReadIndex(stream, setting->rigidbody_index_size); + stream->read((char*) this->position, sizeof(float) * 3); + stream->read((char*) this->orientaiton, sizeof(float) * 3); + stream->read((char*) this->move_limitation_min, sizeof(float) * 3); + stream->read((char*) this->move_limitation_max, sizeof(float) * 3); + stream->read((char*) this->rotation_limitation_min, sizeof(float) * 3); + stream->read((char*) this->rotation_limitation_max, sizeof(float) * 3); + stream->read((char*) this->spring_move_coefficient, sizeof(float) * 3); + stream->read((char*) this->spring_rotation_coefficient, sizeof(float) * 3); + } + + void PmxJoint::Read(std::istream *stream, PmxSetting *setting) + { + this->joint_name = ReadString(stream, setting->encoding); + this->joint_english_name = ReadString(stream, setting->encoding); + stream->read((char*) &this->joint_type, sizeof(uint8_t)); + this->param.Read(stream, setting); + } + + void PmxAncherRigidBody::Read(std::istream *stream, PmxSetting *setting) + { + this->related_rigid_body = ReadIndex(stream, setting->rigidbody_index_size); + this->related_vertex = ReadIndex(stream, setting->vertex_index_size); + stream->read((char*) &this->is_near, sizeof(uint8_t)); + } + + void PmxSoftBody::Read(std::istream *stream, PmxSetting *setting) + { + // 未実装 + std::cerr << "Not Implemented Exception" << std::endl; + throw; + } + + void PmxModel::Init() + { + this->version = 0.0f; + this->model_name.clear(); + this->model_english_name.clear(); + this->model_comment.clear(); + this->model_english_comment.clear(); + this->vertex_count = 0; + this->vertices = nullptr; + this->index_count = 0; + this->indices = nullptr; + this->texture_count = 0; + this->textures = nullptr; + this->material_count = 0; + this->materials = nullptr; + this->bone_count = 0; + this->bones = nullptr; + this->morph_count = 0; + this->morphs = nullptr; + this->frame_count = 0; + this->frames = nullptr; + this->rigid_body_count = 0; + this->rigid_bodies = nullptr; + this->joint_count = 0; + this->joints = nullptr; + this->soft_body_count = 0; + this->soft_bodies = nullptr; + } + + void PmxModel::Read(std::istream *stream) + { + // マジック + char magic[4]; + stream->read((char*) magic, sizeof(char) * 4); + if (magic[0] != 0x50 || magic[1] != 0x4d || magic[2] != 0x58 || magic[3] != 0x20) + { + std::cerr << "invalid magic number." << std::endl; + throw; + } + // バージョン + stream->read((char*) &version, sizeof(float)); + if (version != 2.0f && version != 2.1f) + { + std::cerr << "this is not ver2.0 or ver2.1 but " << version << "." << std::endl; + throw; + } + // ファイル設定 + this->setting.Read(stream); + + // モデル情報 + this->model_name = std::move(ReadString(stream, setting.encoding)); + this->model_english_name = std::move(ReadString(stream, setting.encoding)); + this->model_comment = std::move(ReadString(stream, setting.encoding)); + this->model_english_comment = std::move(ReadString(stream, setting.encoding)); + + // 頂点 + stream->read((char*) &vertex_count, sizeof(int)); + this->vertices = mmd::make_unique(vertex_count); + for (int i = 0; i < vertex_count; i++) + { + vertices[i].Read(stream, &setting); + } + + // 面 + stream->read((char*) &index_count, sizeof(int)); + this->indices = mmd::make_unique(index_count); + for (int i = 0; i < index_count; i++) + { + this->indices[i] = ReadIndex(stream, setting.vertex_index_size); + } + + // テクスチャ + stream->read((char*) &texture_count, sizeof(int)); + this->textures = mmd::make_unique(texture_count); + for (int i = 0; i < texture_count; i++) + { + this->textures[i] = ReadString(stream, setting.encoding); + } + + // マテリアル + stream->read((char*) &material_count, sizeof(int)); + this->materials = mmd::make_unique(material_count); + for (int i = 0; i < material_count; i++) + { + this->materials[i].Read(stream, &setting); + } + + // ボーン + stream->read((char*) &this->bone_count, sizeof(int)); + this->bones = mmd::make_unique(this->bone_count); + for (int i = 0; i < this->bone_count; i++) + { + this->bones[i].Read(stream, &setting); + } + + // モーフ + stream->read((char*) &this->morph_count, sizeof(int)); + this->morphs = mmd::make_unique(this->morph_count); + for (int i = 0; i < this->morph_count; i++) + { + this->morphs[i].Read(stream, &setting); + } + + // 表示枠 + stream->read((char*) &this->frame_count, sizeof(int)); + this->frames = mmd::make_unique(this->frame_count); + for (int i = 0; i < this->frame_count; i++) + { + this->frames[i].Read(stream, &setting); + } + + // 剛体 + stream->read((char*) &this->rigid_body_count, sizeof(int)); + this->rigid_bodies = mmd::make_unique(this->rigid_body_count); + for (int i = 0; i < this->rigid_body_count; i++) + { + this->rigid_bodies[i].Read(stream, &setting); + } + + // ジョイント + stream->read((char*) &this->joint_count, sizeof(int)); + this->joints = mmd::make_unique(this->joint_count); + for (int i = 0; i < this->joint_count; i++) + { + this->joints[i].Read(stream, &setting); + } + + //// ソフトボディ + //if (this->version == 2.1f) + //{ + // stream->read((char*) &this->soft_body_count, sizeof(int)); + // this->soft_bodies = mmd::make_unique(this->soft_body_count); + // for (int i = 0; i < this->soft_body_count; i++) + // { + // this->soft_bodies[i].Read(stream, &setting); + // } + //} + } + + //std::unique_ptr ReadFromFile(const char *filename) + //{ + // auto stream = std::ifstream(filename, std::ios_base::binary); + // auto pmx = PmxModel::ReadFromStream(&stream); + // if (!stream.eof()) + // { + // std::cerr << "don't reach the end of file." << std::endl; + // } + // stream.close(); + // return pmx; + //} + + //std::unique_ptr ReadFromStream(std::istream *stream) + //{ + // auto pmx = mmd::make_unique(); + // pmx->Read(stream); + // return pmx; + //} +} diff --git a/code/MMDPmxParser.h b/code/MMDPmxParser.h new file mode 100644 index 000000000..7cb94b9b6 --- /dev/null +++ b/code/MMDPmxParser.h @@ -0,0 +1,860 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "MMDCpp14.h" + +namespace pmx +{ + /// インデックス設定 + class PmxSetting + { + public: + PmxSetting() + : encoding(0) + , uv(0) + , vertex_index_size(0) + , texture_index_size(0) + , material_index_size(0) + , bone_index_size(0) + , morph_index_size(0) + , rigidbody_index_size(0) + {} + + /// エンコード方式 + uint8_t encoding; + /// 追加UV数 + uint8_t uv; + /// 頂点インデックスサイズ + uint8_t vertex_index_size; + /// テクスチャインデックスサイズ + uint8_t texture_index_size; + /// マテリアルインデックスサイズ + uint8_t material_index_size; + /// ボーンインデックスサイズ + uint8_t bone_index_size; + /// モーフインデックスサイズ + uint8_t morph_index_size; + /// 剛体インデックスサイズ + uint8_t rigidbody_index_size; + void Read(std::istream *stream); + }; + + /// 頂点スキニングタイプ + enum class PmxVertexSkinningType : uint8_t + { + BDEF1 = 0, + BDEF2 = 1, + BDEF4 = 2, + SDEF = 3, + QDEF = 4, + }; + + /// 頂点スキニング + class PmxVertexSkinning + { + public: + virtual void Read(std::istream *stream, PmxSetting *setting) = 0; + }; + + class PmxVertexSkinningBDEF1 : public PmxVertexSkinning + { + public: + PmxVertexSkinningBDEF1() + : bone_index(0) + {} + + int bone_index; + void Read(std::istream *stresam, PmxSetting *setting); + }; + + class PmxVertexSkinningBDEF2 : public PmxVertexSkinning + { + public: + PmxVertexSkinningBDEF2() + : bone_index1(0) + , bone_index2(0) + , bone_weight(0.0f) + {} + + int bone_index1; + int bone_index2; + float bone_weight; + void Read(std::istream *stresam, PmxSetting *setting); + }; + + class PmxVertexSkinningBDEF4 : public PmxVertexSkinning + { + public: + PmxVertexSkinningBDEF4() + : bone_index1(0) + , bone_index2(0) + , bone_index3(0) + , bone_index4(0) + , bone_weight1(0.0f) + , bone_weight2(0.0f) + , bone_weight3(0.0f) + , bone_weight4(0.0f) + {} + + int bone_index1; + int bone_index2; + int bone_index3; + int bone_index4; + float bone_weight1; + float bone_weight2; + float bone_weight3; + float bone_weight4; + void Read(std::istream *stresam, PmxSetting *setting); + }; + + class PmxVertexSkinningSDEF : public PmxVertexSkinning + { + public: + PmxVertexSkinningSDEF() + : bone_index1(0) + , bone_index2(0) + , bone_weight(0.0f) + { + for (int i = 0; i < 3; ++i) { + sdef_c[i] = 0.0f; + sdef_r0[i] = 0.0f; + sdef_r1[i] = 0.0f; + } + } + + int bone_index1; + int bone_index2; + float bone_weight; + float sdef_c[3]; + float sdef_r0[3]; + float sdef_r1[3]; + void Read(std::istream *stresam, PmxSetting *setting); + }; + + class PmxVertexSkinningQDEF : public PmxVertexSkinning + { + public: + PmxVertexSkinningQDEF() + : bone_index1(0) + , bone_index2(0) + , bone_index3(0) + , bone_index4(0) + , bone_weight1(0.0f) + , bone_weight2(0.0f) + , bone_weight3(0.0f) + , bone_weight4(0.0f) + {} + + int bone_index1; + int bone_index2; + int bone_index3; + int bone_index4; + float bone_weight1; + float bone_weight2; + float bone_weight3; + float bone_weight4; + void Read(std::istream *stresam, PmxSetting *setting); + }; + + /// 頂点 + class PmxVertex + { + public: + PmxVertex() + : edge(0.0f) + { + uv[0] = uv[1] = 0.0f; + for (int i = 0; i < 3; ++i) { + position[i] = 0.0f; + normal[i] = 0.0f; + } + for (int i = 0; i < 4; ++i) { + for (int k = 0; k < 4; ++k) { + uva[i][k] = 0.0f; + } + } + } + + /// 位置 + float position[3]; + /// 法線 + float normal[3]; + /// テクスチャ座標 + float uv[2]; + /// 追加テクスチャ座標 + float uva[4][4]; + /// スキニングタイプ + PmxVertexSkinningType skinning_type; + /// スキニング + std::unique_ptr skinning; + /// エッジ倍率 + float edge; + void Read(std::istream *stream, PmxSetting *setting); + }; + + /// マテリアル + class PmxMaterial + { + public: + PmxMaterial() + : specularlity(0.0f) + , flag(0) + , edge_size(0.0f) + , diffuse_texture_index(0) + , sphere_texture_index(0) + , sphere_op_mode(0) + , common_toon_flag(0) + , toon_texture_index(0) + , index_count(0) + { + for (int i = 0; i < 3; ++i) { + specular[i] = 0.0f; + ambient[i] = 0.0f; + edge_color[i] = 0.0f; + } + for (int i = 0; i < 4; ++i) { + diffuse[i] = 0.0f; + } + } + + /// モデル名 + std::string material_name; + /// モデル英名 + std::string material_english_name; + /// 減衰色 + float diffuse[4]; + /// 光沢色 + float specular[3]; + /// 光沢度 + float specularlity; + /// 環境色 + float ambient[3]; + /// 描画フラグ + uint8_t flag; + /// エッジ色 + float edge_color[4]; + /// エッジサイズ + float edge_size; + /// アルベドテクスチャインデックス + int diffuse_texture_index; + /// スフィアテクスチャインデックス + int sphere_texture_index; + /// スフィアテクスチャ演算モード + uint8_t sphere_op_mode; + /// 共有トゥーンフラグ + uint8_t common_toon_flag; + /// トゥーンテクスチャインデックス + int toon_texture_index; + /// メモ + std::string memo; + /// 頂点インデックス数 + int index_count; + void Read(std::istream *stream, PmxSetting *setting); + }; + + /// リンク + class PmxIkLink + { + public: + PmxIkLink() + : link_target(0) + , angle_lock(0) + { + for (int i = 0; i < 3; ++i) { + max_radian[i] = 0.0f; + min_radian[i] = 0.0f; + } + } + + /// リンクボーンインデックス + int link_target; + /// 角度制限 + uint8_t angle_lock; + /// 最大制限角度 + float max_radian[3]; + /// 最小制限角度 + float min_radian[3]; + void Read(std::istream *stream, PmxSetting *settingn); + }; + + /// ボーン + class PmxBone + { + public: + PmxBone() + : parent_index(0) + , level(0) + , bone_flag(0) + , target_index(0) + , grant_parent_index(0) + , grant_weight(0.0f) + , key(0) + , ik_target_bone_index(0) + , ik_loop(0) + , ik_loop_angle_limit(0.0f) + , ik_link_count(0) + { + for (int i = 0; i < 3; ++i) { + position[i] = 0.0f; + offset[i] = 0.0f; + lock_axis_orientation[i] = 0.0f; + local_axis_x_orientation[i] = 0.0f; + local_axis_y_orientation[i] = 0.0f; + } + } + + /// ボーン名 + std::string bone_name; + /// ボーン英名 + std::string bone_english_name; + /// 位置 + float position[3]; + /// 親ボーンインデックス + int parent_index; + /// 階層 + int level; + /// ボーンフラグ + uint16_t bone_flag; + /// 座標オフセット(has Target) + float offset[3]; + /// 接続先ボーンインデックス(not has Target) + int target_index; + /// 付与親ボーンインデックス + int grant_parent_index; + /// 付与率 + float grant_weight; + /// 固定軸の方向 + float lock_axis_orientation[3]; + /// ローカル軸のX軸方向 + float local_axis_x_orientation[3]; + /// ローカル軸のY軸方向 + float local_axis_y_orientation[3]; + /// 外部親変形のkey値 + int key; + /// IKターゲットボーン + int ik_target_bone_index; + /// IKループ回数 + int ik_loop; + /// IKループ計算時の角度制限(ラジアン) + float ik_loop_angle_limit; + /// IKリンク数 + int ik_link_count; + /// IKリンク + std::unique_ptr ik_links; + void Read(std::istream *stream, PmxSetting *setting); + }; + + enum class MorphType : uint8_t + { + Group = 0, + Vertex = 1, + Bone = 2, + UV = 3, + AdditionalUV1 = 4, + AdditionalUV2 = 5, + AdditionalUV3 = 6, + AdditionalUV4 = 7, + Matrial = 8, + Flip = 9, + Implus = 10, + }; + + enum class MorphCategory : uint8_t + { + ReservedCategory = 0, + Eyebrow = 1, + Eye = 2, + Mouth = 3, + Other = 4, + }; + + class PmxMorphOffset + { + public: + void virtual Read(std::istream *stream, PmxSetting *setting) = 0; + }; + + class PmxMorphVertexOffset : public PmxMorphOffset + { + public: + PmxMorphVertexOffset() + : vertex_index(0) + { + for (int i = 0; i < 3; ++i) { + position_offset[i] = 0.0f; + } + } + int vertex_index; + float position_offset[3]; + void Read(std::istream *stream, PmxSetting *setting); //override; + }; + + class PmxMorphUVOffset : public PmxMorphOffset + { + public: + PmxMorphUVOffset() + : vertex_index(0) + { + for (int i = 0; i < 4; ++i) { + uv_offset[i] = 0.0f; + } + } + int vertex_index; + float uv_offset[4]; + void Read(std::istream *stream, PmxSetting *setting); //override; + }; + + class PmxMorphBoneOffset : public PmxMorphOffset + { + public: + PmxMorphBoneOffset() + : bone_index(0) + { + for (int i = 0; i < 3; ++i) { + translation[i] = 0.0f; + } + for (int i = 0; i < 4; ++i) { + rotation[i] = 0.0f; + } + } + int bone_index; + float translation[3]; + float rotation[4]; + void Read(std::istream *stream, PmxSetting *setting); //override; + }; + + class PmxMorphMaterialOffset : public PmxMorphOffset + { + public: + PmxMorphMaterialOffset() + : specularity(0.0f) + , edge_size(0.0f) + { + for (int i = 0; i < 3; ++i) { + specular[i] = 0.0f; + ambient[i] = 0.0f; + } + for (int i = 0; i < 4; ++i) { + diffuse[i] = 0.0f; + edge_color[i] = 0.0f; + texture_argb[i] = 0.0f; + sphere_texture_argb[i] = 0.0f; + toon_texture_argb[i] = 0.0f; + } + } + int material_index; + uint8_t offset_operation; + float diffuse[4]; + float specular[3]; + float specularity; + float ambient[3]; + float edge_color[4]; + float edge_size; + float texture_argb[4]; + float sphere_texture_argb[4]; + float toon_texture_argb[4]; + void Read(std::istream *stream, PmxSetting *setting); //override; + }; + + class PmxMorphGroupOffset : public PmxMorphOffset + { + public: + PmxMorphGroupOffset() + : morph_index(0) + , morph_weight(0.0f) + {} + int morph_index; + float morph_weight; + void Read(std::istream *stream, PmxSetting *setting); //override; + }; + + class PmxMorphFlipOffset : public PmxMorphOffset + { + public: + PmxMorphFlipOffset() + : morph_index(0) + , morph_value(0.0f) + {} + int morph_index; + float morph_value; + void Read(std::istream *stream, PmxSetting *setting); //override; + }; + + class PmxMorphImplusOffset : public PmxMorphOffset + { + public: + PmxMorphImplusOffset() + : rigid_body_index(0) + , is_local(0) + { + for (int i = 0; i < 3; ++i) { + velocity[i] = 0.0f; + angular_torque[i] = 0.0f; + } + } + int rigid_body_index; + uint8_t is_local; + float velocity[3]; + float angular_torque[3]; + void Read(std::istream *stream, PmxSetting *setting); //override; + }; + + /// モーフ + class PmxMorph + { + public: + PmxMorph() + : offset_count(0) + { + } + /// モーフ名 + std::string morph_name; + /// モーフ英名 + std::string morph_english_name; + /// カテゴリ + MorphCategory category; + /// モーフタイプ + MorphType morph_type; + /// オフセット数 + int offset_count; + /// 頂点モーフ配列 + std::unique_ptr vertex_offsets; + /// UVモーフ配列 + std::unique_ptr uv_offsets; + /// ボーンモーフ配列 + std::unique_ptr bone_offsets; + /// マテリアルモーフ配列 + std::unique_ptr material_offsets; + /// グループモーフ配列 + std::unique_ptr group_offsets; + /// フリップモーフ配列 + std::unique_ptr flip_offsets; + /// インパルスモーフ配列 + std::unique_ptr implus_offsets; + void Read(std::istream *stream, PmxSetting *setting); + }; + + /// 枠内要素 + class PmxFrameElement + { + public: + PmxFrameElement() + : element_target(0) + , index(0) + { + } + /// 要素対象 + uint8_t element_target; + /// 要素対象インデックス + int index; + void Read(std::istream *stream, PmxSetting *setting); + }; + + /// 表示枠 + class PmxFrame + { + public: + PmxFrame() + : frame_flag(0) + , element_count(0) + { + } + /// 枠名 + std::string frame_name; + /// 枠英名 + std::string frame_english_name; + /// 特殊枠フラグ + uint8_t frame_flag; + /// 枠内要素数 + int element_count; + /// 枠内要素配列 + std::unique_ptr elements; + void Read(std::istream *stream, PmxSetting *setting); + }; + + class PmxRigidBody + { + public: + PmxRigidBody() + : target_bone(0) + , group(0) + , mask(0) + , shape(0) + , mass(0.0f) + , move_attenuation(0.0f) + , rotation_attenuation(0.0f) + , repulsion(0.0f) + , friction(0.0f) + , physics_calc_type(0) + { + for (int i = 0; i < 3; ++i) { + size[i] = 0.0f; + position[i] = 0.0f; + orientation[i] = 0.0f; + } + } + /// 剛体名 + std::string girid_body_name; + /// 剛体英名 + std::string girid_body_english_name; + /// 関連ボーンインデックス + int target_bone; + /// グループ + uint8_t group; + /// マスク + uint16_t mask; + /// 形状 + uint8_t shape; + float size[3]; + float position[3]; + float orientation[3]; + float mass; + float move_attenuation; + float rotation_attenuation; + float repulsion; + float friction; + uint8_t physics_calc_type; + void Read(std::istream *stream, PmxSetting *setting); + }; + + enum class PmxJointType : uint8_t + { + Generic6DofSpring = 0, + Generic6Dof = 1, + Point2Point = 2, + ConeTwist = 3, + Slider = 5, + Hinge = 6 + }; + + class PmxJointParam + { + public: + PmxJointParam() + : rigid_body1(0) + , rigid_body2(0) + { + for (int i = 0; i < 3; ++i) { + position[i] = 0.0f; + orientaiton[i] = 0.0f; + move_limitation_min[i] = 0.0f; + move_limitation_max[i] = 0.0f; + rotation_limitation_min[i] = 0.0f; + rotation_limitation_max[i] = 0.0f; + spring_move_coefficient[i] = 0.0f; + spring_rotation_coefficient[i] = 0.0f; + } + } + int rigid_body1; + int rigid_body2; + float position[3]; + float orientaiton[3]; + float move_limitation_min[3]; + float move_limitation_max[3]; + float rotation_limitation_min[3]; + float rotation_limitation_max[3]; + float spring_move_coefficient[3]; + float spring_rotation_coefficient[3]; + void Read(std::istream *stream, PmxSetting *setting); + }; + + class PmxJoint + { + public: + std::string joint_name; + std::string joint_english_name; + PmxJointType joint_type; + PmxJointParam param; + void Read(std::istream *stream, PmxSetting *setting); + }; + + enum PmxSoftBodyFlag : uint8_t + { + BLink = 0x01, + Cluster = 0x02, + Link = 0x04 + }; + + class PmxAncherRigidBody + { + public: + PmxAncherRigidBody() + : related_rigid_body(0) + , related_vertex(0) + , is_near(false) + {} + int related_rigid_body; + int related_vertex; + bool is_near; + void Read(std::istream *stream, PmxSetting *setting); + }; + + class PmxSoftBody + { + public: + PmxSoftBody() + : shape(0) + , target_material(0) + , group(0) + , mask(0) + , blink_distance(0) + , cluster_count(0) + , mass(0.0) + , collisioni_margin(0.0) + , aero_model(0) + , VCF(0.0f) + , DP(0.0f) + , DG(0.0f) + , LF(0.0f) + , PR(0.0f) + , VC(0.0f) + , DF(0.0f) + , MT(0.0f) + , CHR(0.0f) + , KHR(0.0f) + , SHR(0.0f) + , AHR(0.0f) + , SRHR_CL(0.0f) + , SKHR_CL(0.0f) + , SSHR_CL(0.0f) + , SR_SPLT_CL(0.0f) + , SK_SPLT_CL(0.0f) + , SS_SPLT_CL(0.0f) + , V_IT(0) + , P_IT(0) + , D_IT(0) + , C_IT(0) + , LST(0.0f) + , AST(0.0f) + , VST(0.0f) + , anchor_count(0) + , pin_vertex_count(0) + {} + std::string soft_body_name; + std::string soft_body_english_name; + uint8_t shape; + int target_material; + uint8_t group; + uint16_t mask; + PmxSoftBodyFlag flag; + int blink_distance; + int cluster_count; + float mass; + float collisioni_margin; + int aero_model; + 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; + int V_IT; + int P_IT; + int D_IT; + int C_IT; + float LST; + float AST; + float VST; + int anchor_count; + std::unique_ptr anchers; + int pin_vertex_count; + std::unique_ptr pin_vertices; + void Read(std::istream *stream, PmxSetting *setting); + }; + + /// PMXモデル + class PmxModel + { + public: + PmxModel() + : version(0.0f) + , vertex_count(0) + , index_count(0) + , texture_count(0) + , material_count(0) + , bone_count(0) + , morph_count(0) + , frame_count(0) + , rigid_body_count(0) + , joint_count(0) + , soft_body_count(0) + {} + + /// バージョン + float version; + /// 設定 + PmxSetting setting; + /// モデル名 + std::string model_name; + /// モデル英名 + std::string model_english_name; + /// コメント + std::string model_comment; + /// 英語コメント + std::string model_english_comment; + /// 頂点数 + int vertex_count; + /// 頂点配列 + std::unique_ptr vertices; + /// インデックス数 + int index_count; + /// インデックス配列 + std::unique_ptr indices; + /// テクスチャ数 + int texture_count; + /// テクスチャ配列 + std::unique_ptr< std::string []> textures; + /// マテリアル数 + int material_count; + /// マテリアル + std::unique_ptr materials; + /// ボーン数 + int bone_count; + /// ボーン配列 + std::unique_ptr bones; + /// モーフ数 + int morph_count; + /// モーフ配列 + std::unique_ptr morphs; + /// 表示枠数 + int frame_count; + /// 表示枠配列 + std::unique_ptr frames; + /// 剛体数 + int rigid_body_count; + /// 剛体配列 + std::unique_ptr rigid_bodies; + /// ジョイント数 + int joint_count; + /// ジョイント配列 + std::unique_ptr joints; + /// ソフトボディ数 + int soft_body_count; + /// ソフトボディ配列 + std::unique_ptr soft_bodies; + /// モデル初期化 + void Init(); + /// モデル読み込み + void Read(std::istream *stream); + ///// ファイルからモデルの読み込み + //static std::unique_ptr ReadFromFile(const char *filename); + ///// 入力ストリームからモデルの読み込み + //static std::unique_ptr ReadFromStream(std::istream *stream); + }; +} diff --git a/code/MMDVmdParser.h b/code/MMDVmdParser.h new file mode 100644 index 000000000..380a10c5d --- /dev/null +++ b/code/MMDVmdParser.h @@ -0,0 +1,367 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include "MMDCpp14.h" + +namespace vmd +{ + /// ボーンフレーム + class VmdBoneFrame + { + public: + /// ボーン名 + std::string name; + /// フレーム番号 + int frame; + /// 位置 + float position[3]; + /// 回転 + float orientation[4]; + /// 補間曲線 + char interpolation[4][4][4]; + + void Read(std::istream* stream) + { + char buffer[15]; + stream->read((char*) buffer, sizeof(char)*15); + name = std::string(buffer); + stream->read((char*) &frame, sizeof(int)); + stream->read((char*) position, sizeof(float)*3); + stream->read((char*) orientation, sizeof(float)*4); + stream->read((char*) interpolation, sizeof(char) * 4 * 4 * 4); + } + + void Write(std::ostream* stream) + { + stream->write((char*)name.c_str(), sizeof(char) * 15); + stream->write((char*)&frame, sizeof(int)); + stream->write((char*)position, sizeof(float) * 3); + stream->write((char*)orientation, sizeof(float) * 4); + stream->write((char*)interpolation, sizeof(char) * 4 * 4 * 4); + } + }; + + /// 表情フレーム + class VmdFaceFrame + { + public: + /// 表情名 + std::string face_name; + /// 表情の重み + float weight; + /// フレーム番号 + uint32_t frame; + + void Read(std::istream* stream) + { + char buffer[15]; + stream->read((char*) &buffer, sizeof(char) * 15); + face_name = std::string(buffer); + stream->read((char*) &frame, sizeof(int)); + stream->read((char*) &weight, sizeof(float)); + } + + void Write(std::ostream* stream) + { + stream->write((char*)face_name.c_str(), sizeof(char) * 15); + stream->write((char*)&frame, sizeof(int)); + stream->write((char*)&weight, sizeof(float)); + } + }; + + /// カメラフレーム + class VmdCameraFrame + { + public: + /// フレーム番号 + int frame; + /// 距離 + float distance; + /// 位置 + float position[3]; + /// 回転 + float orientation[3]; + /// 補間曲線 + char interpolation[6][4]; + /// 視野角 + float angle; + /// 不明データ + char unknown[3]; + + void Read(std::istream *stream) + { + stream->read((char*) &frame, sizeof(int)); + stream->read((char*) &distance, sizeof(float)); + stream->read((char*) position, sizeof(float) * 3); + stream->read((char*) orientation, sizeof(float) * 3); + stream->read((char*) interpolation, sizeof(char) * 24); + stream->read((char*) &angle, sizeof(float)); + stream->read((char*) unknown, sizeof(char) * 3); + } + + void Write(std::ostream *stream) + { + stream->write((char*)&frame, sizeof(int)); + stream->write((char*)&distance, sizeof(float)); + stream->write((char*)position, sizeof(float) * 3); + stream->write((char*)orientation, sizeof(float) * 3); + stream->write((char*)interpolation, sizeof(char) * 24); + stream->write((char*)&angle, sizeof(float)); + stream->write((char*)unknown, sizeof(char) * 3); + } + }; + + /// ライトフレーム + class VmdLightFrame + { + public: + /// フレーム番号 + int frame; + /// 色 + float color[3]; + /// 位置 + float position[3]; + + void Read(std::istream* stream) + { + stream->read((char*) &frame, sizeof(int)); + stream->read((char*) color, sizeof(float) * 3); + stream->read((char*) position, sizeof(float) * 3); + } + + void Write(std::ostream* stream) + { + stream->write((char*)&frame, sizeof(int)); + stream->write((char*)color, sizeof(float) * 3); + stream->write((char*)position, sizeof(float) * 3); + } + }; + + /// IKの有効無効 + class VmdIkEnable + { + public: + std::string ik_name; + bool enable; + }; + + /// IKフレーム + class VmdIkFrame + { + public: + int frame; + bool display; + std::vector ik_enable; + + void Read(std::istream *stream) + { + char buffer[20]; + stream->read((char*) &frame, sizeof(int)); + stream->read((char*) &display, sizeof(uint8_t)); + int ik_count; + stream->read((char*) &ik_count, sizeof(int)); + ik_enable.resize(ik_count); + for (int i = 0; i < ik_count; i++) + { + stream->read(buffer, 20); + ik_enable[i].ik_name = std::string(buffer); + stream->read((char*) &ik_enable[i].enable, sizeof(uint8_t)); + } + } + + void Write(std::ostream *stream) + { + stream->write((char*)&frame, sizeof(int)); + stream->write((char*)&display, sizeof(uint8_t)); + int ik_count = static_cast(ik_enable.size()); + stream->write((char*)&ik_count, sizeof(int)); + for (int i = 0; i < ik_count; i++) + { + const VmdIkEnable& ik_enable = this->ik_enable.at(i); + stream->write(ik_enable.ik_name.c_str(), 20); + stream->write((char*)&ik_enable.enable, sizeof(uint8_t)); + } + } + }; + + /// VMDモーション + class VmdMotion + { + public: + /// モデル名 + std::string model_name; + /// バージョン + int version; + /// ボーンフレーム + std::vector bone_frames; + /// 表情フレーム + std::vector face_frames; + /// カメラフレーム + std::vector camera_frames; + /// ライトフレーム + std::vector light_frames; + /// IKフレーム + std::vector ik_frames; + + static std::unique_ptr LoadFromFile(char const *filename) + { + std::ifstream stream(filename, std::ios::binary); + auto result = LoadFromStream(&stream); + stream.close(); + return result; + } + + static std::unique_ptr LoadFromStream(std::ifstream *stream) + { + + char buffer[30]; + auto result = mmd::make_unique(); + + // magic and version + stream->read((char*) buffer, 30); + if (strncmp(buffer, "Vocaloid Motion Data", 20)) + { + std::cerr << "invalid vmd file." << std::endl; + return nullptr; + } + result->version = std::atoi(buffer + 20); + + // name + stream->read(buffer, 20); + result->model_name = std::string(buffer); + + // bone frames + int bone_frame_num; + stream->read((char*) &bone_frame_num, sizeof(int)); + result->bone_frames.resize(bone_frame_num); + for (int i = 0; i < bone_frame_num; i++) + { + result->bone_frames[i].Read(stream); + } + + // face frames + int face_frame_num; + stream->read((char*) &face_frame_num, sizeof(int)); + result->face_frames.resize(face_frame_num); + for (int i = 0; i < face_frame_num; i++) + { + result->face_frames[i].Read(stream); + } + + // camera frames + int camera_frame_num; + stream->read((char*) &camera_frame_num, sizeof(int)); + result->camera_frames.resize(camera_frame_num); + for (int i = 0; i < camera_frame_num; i++) + { + result->camera_frames[i].Read(stream); + } + + // light frames + int light_frame_num; + stream->read((char*) &light_frame_num, sizeof(int)); + result->light_frames.resize(light_frame_num); + for (int i = 0; i < light_frame_num; i++) + { + result->light_frames[i].Read(stream); + } + + // unknown2 + stream->read(buffer, 4); + + // ik frames + if (stream->peek() != std::ios::traits_type::eof()) + { + int ik_num; + stream->read((char*) &ik_num, sizeof(int)); + result->ik_frames.resize(ik_num); + for (int i = 0; i < ik_num; i++) + { + result->ik_frames[i].Read(stream); + } + } + + if (stream->peek() != std::ios::traits_type::eof()) + { + std::cerr << "vmd stream has unknown data." << std::endl; + } + + return result; + } + + bool SaveToFile(const std::u16string& filename) + { + // TODO: How to adapt u16string to string? + /* + std::ofstream stream(filename.c_str(), std::ios::binary); + auto result = SaveToStream(&stream); + stream.close(); + return result; + */ + return false; + } + + bool SaveToStream(std::ofstream *stream) + { + std::string magic = "Vocaloid Motion Data 0002\0"; + magic.resize(30); + + // magic and version + stream->write(magic.c_str(), 30); + + // name + stream->write(model_name.c_str(), 20); + + // bone frames + const int bone_frame_num = static_cast(bone_frames.size()); + stream->write(reinterpret_cast(&bone_frame_num), sizeof(int)); + for (int i = 0; i < bone_frame_num; i++) + { + bone_frames[i].Write(stream); + } + + // face frames + const int face_frame_num = static_cast(face_frames.size()); + stream->write(reinterpret_cast(&face_frame_num), sizeof(int)); + for (int i = 0; i < face_frame_num; i++) + { + face_frames[i].Write(stream); + } + + // camera frames + const int camera_frame_num = static_cast(camera_frames.size()); + stream->write(reinterpret_cast(&camera_frame_num), sizeof(int)); + for (int i = 0; i < camera_frame_num; i++) + { + camera_frames[i].Write(stream); + } + + // light frames + const int light_frame_num = static_cast(light_frames.size()); + stream->write(reinterpret_cast(&light_frame_num), sizeof(int)); + for (int i = 0; i < light_frame_num; i++) + { + light_frames[i].Write(stream); + } + + // self shadow datas + const int self_shadow_num = 0; + stream->write(reinterpret_cast(&self_shadow_num), sizeof(int)); + + // ik frames + const int ik_num = static_cast(ik_frames.size()); + stream->write(reinterpret_cast(&ik_num), sizeof(int)); + for (int i = 0; i < ik_num; i++) + { + ik_frames[i].Write(stream); + } + + return true; + } + }; +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 315747772..da8a122d3 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -108,6 +108,7 @@ SET( TEST_SRCS unit/utObjImportExport.cpp unit/utPretransformVertices.cpp unit/utPLYImportExport.cpp + unit/utPMXImporter.cpp unit/utRemoveComments.cpp unit/utRemoveComponent.cpp unit/utRemoveRedundantMaterials.cpp diff --git a/test/models-nonbsd/MMD/Alicia_blade.pmx b/test/models-nonbsd/MMD/Alicia_blade.pmx new file mode 100644 index 000000000..cdd7709cc Binary files /dev/null and b/test/models-nonbsd/MMD/Alicia_blade.pmx differ diff --git a/test/models-nonbsd/MMD/readme.txt b/test/models-nonbsd/MMD/readme.txt new file mode 100644 index 000000000..d8c882420 --- /dev/null +++ b/test/models-nonbsd/MMD/readme.txt @@ -0,0 +1,49 @@ +———————————————————————— +ニコニ立体公式キャラクター「ニコニ立体ちゃん」 +http://3d.nicovideo.jp/alicia/ +version 4 +©dwango, inc. All rights reserved. +———————————————————————— + + +この度は、ニコニ立体公式キャラクター「ニコニ立体ちゃん」をダウンロードいただきましてありがとうございます。 + +◯ 内容物 +1. MikuMikuDance(MMD)用ファイル2種 (ニコニ立体ちゃん本体・ビーム彫刻刀) +2. MMD用モーションファイル16種 (ニコファーレのモーションキャプチャーデバイスを用いて収録) +2. FBXファイル2種 (MMD用データ、Unity用データ) + MAXファイル(オリジナルデータ) +3. Unity Package1種 +4. ニコニ立体ちゃん壁紙1種 + + +◯よくある質問 +* 「ニコニ立体ちゃん」と「アリシア・ソリッド」、正式名称はどちら? +キャラクターの本名はアリシア・ソリッドですが、クレジット等での表記はニコニ立体ちゃんが正式名称となります。 +作品発表の際には「ニコニ立体ちゃん」タグをご活用ください。 + +* ニコニ立体ちゃんを用いた、年齢制限のある二次創作物の公開は可能なのか? +利用規約にある禁止事項に抵触しない限りにおいて可能です。 + + +◯ 利用規約抜粋 +必ず利用規約( http://3d.nicovideo.jp/alicia/rule.html )をご確認ください。 +利用規約の改訂などによって下記抜粋と利用規約の内容が異なる場合は利用規約が優先されます。 + +* 非営利、営利に関わらず個人(同人サークルなど、法人を除く団体を含む)の利用が可能です。 +* 二次創作物について株式会社ドワンゴ(以下、当社)のクレジットを表記する必要はありません。 +* 改変物を配布することができます。 +* niconico内でキャラクターやモーションを利用した作品を投稿する場合は、コンテンツツリーの親作品にキャラクター( http://3d.nicovideo.jp/works/td14712 )を登録するよう努めるものとします。 + + +◯禁止事項 +* 第三者の知的財産権その他一切の権利及び名誉を侵害しないこと。 +* 当社(当社が提供するサービス等を含む)及び当社キャラクターの名誉・品位傷つける行為をしないこと。 +* 公序良俗に反する行為や目的、暴力的な表現、反社会的な行為や目的、特定の信条や宗教、政治的発言のため利用しないこと。 +* 当社の公式商品であるかのような誤解を招く利用をしないこと。 +* その他、当社が不適切と判断する行為に利用しないこと。 + + +◯ クレジット +企画: 株式会社ドワンゴ +キャラクターデザイン: 黒星紅白 +モデリング: 雨刻 \ No newline at end of file diff --git a/test/unit/utPMXImporter.cpp b/test/unit/utPMXImporter.cpp new file mode 100644 index 000000000..725279e47 --- /dev/null +++ b/test/unit/utPMXImporter.cpp @@ -0,0 +1,62 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2016, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +#include "UnitTestPCH.h" +#include "SceneDiffer.h" +#include "AbstractImportExportBase.h" +#include "MMDImporter.h" + +#include + +using namespace ::Assimp; + +class utPMXImporter : public AbstractImportExportBase { +public: + virtual bool importerTest() { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/../models-nonbsd/MMD/Alicia_blade.pmx", 0 ); + return nullptr != scene; + } +}; + +TEST_F( utPMXImporter, importTest ) { + EXPECT_TRUE( importerTest() ); +} diff --git a/tools/assimp_qt_viewer/CMakeLists.txt b/tools/assimp_qt_viewer/CMakeLists.txt index 2985d6e30..42ef0fb34 100644 --- a/tools/assimp_qt_viewer/CMakeLists.txt +++ b/tools/assimp_qt_viewer/CMakeLists.txt @@ -1,5 +1,5 @@ -project(assimp_qt_viewer) set(PROJECT_VERSION "") +project(assimp_qt_viewer) cmake_minimum_required(VERSION 2.6) diff --git a/tools/assimp_qt_viewer/mainwindow.cpp b/tools/assimp_qt_viewer/mainwindow.cpp index cb4c4a67e..9b6e231bd 100644 --- a/tools/assimp_qt_viewer/mainwindow.cpp +++ b/tools/assimp_qt_viewer/mainwindow.cpp @@ -295,7 +295,7 @@ aiReturn rv; // begin export time_begin = QTime::currentTime(); - rv = exporter.Export(mScene, format_id.toLocal8Bit(), filename.toLocal8Bit()); + rv = exporter.Export(mScene, format_id.toLocal8Bit(), filename.toLocal8Bit(), aiProcess_FlipUVs); ui->lblExportTime->setText(QString("%1").arg(time_begin.secsTo(QTime::currentTime()))); if(rv == aiReturn_SUCCESS) LogInfo("Export done: " + filename);