Parcourir la source

Merge branch 'master' of https://github.com/assimp/assimp

Kim Kulling il y a 8 ans
Parent
commit
1b4cbcc6ad

+ 72 - 0
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)

+ 7 - 3
code/AssbinLoader.cpp

@@ -198,7 +198,7 @@ template <typename T> 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<uint32_t>(stream);
     ai_assert(chunkID == ASSBIN_CHUNK_AINODE);
@@ -210,6 +210,10 @@ void AssbinImporter::ReadBinaryNode( IOStream * stream, aiNode** node )
     (*node)->mTransformation = Read<aiMatrix4x4>(stream);
     (*node)->mNumChildren = Read<unsigned int>(stream);
     (*node)->mNumMeshes = Read<unsigned int>(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)

+ 1 - 1
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);

+ 2 - 1
code/BlenderLoader.cpp

@@ -110,6 +110,7 @@ BlenderImporter::~BlenderImporter()
 }
 
 static const char* Tokens[] = { "BLENDER" };
+static const char* TokensForSearch[] = { "blender" };
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
@@ -122,7 +123,7 @@ bool BlenderImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, b
 
     else if ((!extension.length() || checkSig) && pIOHandler)   {
         // note: this won't catch compressed files
-        return SearchFileHeaderForToken(pIOHandler,pFile, Tokens,1);
+        return SearchFileHeaderForToken(pIOHandler,pFile, TokensForSearch,1);
     }
     return false;
 }

+ 10 - 0
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

+ 239 - 30
code/ColladaExporter.cpp

@@ -58,6 +58,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <memory>
 #include <ctime>
 #include <set>
+#include <vector>
+#include <iostream>
 
 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 << "<library_controllers>" << endstr;
+    PushTag();
+    
+    for( size_t a = 0; a < mScene->mNumMeshes; ++a)
+        WriteController( a);
+
+    PopTag();
+    mOutput << startstr << "</library_controllers>" << 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 << "<controller id=\"" << idstrEscaped << "-skin\" ";
+    mOutput << "name=\"skinCluster" << pIndex << "\">"<< endstr;
+    PushTag();
+
+    mOutput << startstr << "<skin source=\"#" << idstrEscaped << "\">" << endstr;
+    PushTag();
+
+    // bind pose matrix
+    mOutput << startstr << "<bind_shape_matrix>" << 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 << "</bind_shape_matrix>" << endstr;
+
+    mOutput << startstr << "<source id=\"" << idstrEscaped << "-skin-joints\" name=\"" << idstrEscaped << "-skin-joints\">" << endstr;
+    PushTag();
+
+    mOutput << startstr << "<Name_array id=\"" << idstrEscaped << "-skin-joints-array\" count=\"" << mesh->mNumBones << "\">";
+
+    for( size_t i = 0; i < mesh->mNumBones; ++i )
+        mOutput << XMLEscape(mesh->mBones[i]->mName.C_Str()) << " ";
+
+    mOutput << "</Name_array>" << endstr;
+
+    mOutput << startstr << "<technique_common>" << endstr;
+    PushTag();
+    
+    mOutput << startstr << "<accessor source=\"#" << idstrEscaped << "-skin-joints-array\" count=\"" << mesh->mNumBones << "\" stride=\"" << 1 << "\">" << endstr;
+    PushTag();
+
+    mOutput << startstr << "<param name=\"JOINT\" type=\"Name\"></param>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</accessor>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</technique_common>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</source>" << endstr;
+
+    std::vector<ai_real> 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<ai_real> 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 << "<joints>" << endstr;
+    PushTag();
+
+    mOutput << startstr << "<input semantic=\"JOINT\" source=\"#" << idstrEscaped << "-skin-joints\"></input>" << endstr;
+    mOutput << startstr << "<input semantic=\"INV_BIND_MATRIX\" source=\"#" << idstrEscaped << "-skin-bind_poses\"></input>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</joints>" << endstr;
+
+    mOutput << startstr << "<vertex_weights count=\"" << mesh->mNumVertices << "\">" << endstr;
+    PushTag();
+
+    mOutput << startstr << "<input semantic=\"JOINT\" source=\"#" << idstrEscaped << "-skin-joints\" offset=\"0\"></input>" << endstr;
+    mOutput << startstr << "<input semantic=\"WEIGHT\" source=\"#" << idstrEscaped << "-skin-weights\" offset=\"1\"></input>" << endstr;
+
+    mOutput << startstr << "<vcount>";
+
+    std::vector<ai_uint> 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 << "</vcount>" << endstr;
+
+    mOutput << startstr << "<v>";
+
+    ai_uint joint_weight_indices_length = 0;
+    std::vector<ai_uint> 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<ai_int> 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 << "</v>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</vertex_weights>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</skin>" << endstr;
+    
+    PopTag();
+    mOutput << startstr << "</controller>" << 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 << "<param name=\"G\" type=\"float\" />" << endstr;
             mOutput << startstr << "<param name=\"B\" type=\"float\" />" << endstr;
             break;
+
+        case FloatType_Mat4x4:
+            mOutput << startstr << "<param name=\"TRANSFORM\" type=\"float4x4\" />" << endstr;
+            break;
+
+        case FloatType_Weight:
+            mOutput << startstr << "<param name=\"WEIGHT\" type=\"float\" />" << 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
-            << "<node id=\"" << node_name_escaped
-            << "\" name=\"" << node_name_escaped
+            << "<node ";
+    if(is_skeleton_root)
+        mOutput << "id=\"" << "skeleton_root" << "\" "; // For now, only support one skeleton in a scene.
+    mOutput << (is_joint ? "s" : "") << "id=\"" << node_name_escaped;
+    mOutput << "\" name=\"" << node_name_escaped
             << "\" type=\"" << node_type
             << "\">" << 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 << "<matrix>";
+    mOutput << startstr << "<matrix sid=\"transform\">";
     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 << "<instance_geometry url=\"#" << XMLEscape(GetMeshId( pNode->mMeshes[a])) << "\">" << endstr;
-    PushTag();
-    mOutput << startstr << "<bind_material>" << endstr;
-    PushTag();
-    mOutput << startstr << "<technique_common>" << endstr;
-    PushTag();
-    mOutput << startstr << "<instance_material symbol=\"defaultMaterial\" target=\"#" << XMLEscape(materials[mesh->mMaterialIndex].name) << "\">" << endstr;
-    PushTag();
-    for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a )
-    {
-        if( mesh->HasTextureCoords( static_cast<unsigned int>(a) ) )
-            // semantic       as in <texture texcoord=...>
-            // input_semantic as in <input semantic=...>
-            // input_set      as in <input set=...>
-            mOutput << startstr << "<bind_vertex_input semantic=\"CHANNEL" << a << "\" input_semantic=\"TEXCOORD\" input_set=\"" << a << "\"/>" << endstr;
-    }
-    PopTag();
-    mOutput << startstr << "</instance_material>" << endstr;
-    PopTag();
-    mOutput << startstr << "</technique_common>" << endstr;
-    PopTag();
-    mOutput << startstr << "</bind_material>" << endstr;
-    PopTag();
-        mOutput << startstr << "</instance_geometry>" << 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 << "<instance_geometry url=\"#" << XMLEscape(GetMeshId( pNode->mMeshes[a])) << "\">" << endstr;
+            PushTag();
+        }
+        else
+        {
+            mOutput << startstr
+                    << "<instance_controller url=\"#" << XMLEscape(GetMeshId( pNode->mMeshes[a])) << "-skin\">"
+                    << endstr;
+            PushTag();
+
+            mOutput << startstr << "<skeleton>#skeleton_root</skeleton>" << endstr;
+        }
+        mOutput << startstr << "<bind_material>" << endstr;
+        PushTag();
+        mOutput << startstr << "<technique_common>" << endstr;
+        PushTag();
+        mOutput << startstr << "<instance_material symbol=\"defaultMaterial\" target=\"#" << XMLEscape(materials[mesh->mMaterialIndex].name) << "\">" << endstr;
+        PushTag();
+        for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a )
+        {
+            if( mesh->HasTextureCoords( static_cast<unsigned int>(a) ) )
+                // semantic       as in <texture texcoord=...>
+                // input_semantic as in <input semantic=...>
+                // input_set      as in <input set=...>
+                mOutput << startstr << "<bind_vertex_input semantic=\"CHANNEL" << a << "\" input_semantic=\"TEXCOORD\" input_set=\"" << a << "\"/>" << endstr;
+        }
+        PopTag();
+        mOutput << startstr << "</instance_material>" << endstr;
+        PopTag();
+        mOutput << startstr << "</technique_common>" << endstr;
+        PopTag();
+        mOutput << startstr << "</bind_material>" << endstr;
+        
+        PopTag();
+        if( mesh->mNumBones == 0)
+            mOutput << startstr << "</instance_geometry>" << endstr;
+        else
+            mOutput << startstr << "</instance_controller>" << endstr;
     }
 
     // recurse into subnodes

+ 7 - 1
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);

+ 7 - 1
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. */

+ 1 - 1
code/LWOLoader.cpp

@@ -67,7 +67,7 @@ static const aiImporterDesc desc = {
     "LightWave/Modo Object Importer",
     "",
     "",
-    "http://www.newtek.com/lightwave.html\nhttp://www.luxology.com/modo/",
+    "https://www.lightwave3d.com/lightwave_sdk/",
     aiImporterFlags_SupportTextFlavour,
     0,
     0,

+ 0 - 4
code/MDLMaterialLoader.cpp

@@ -546,10 +546,6 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7(
     }
     else if (iMasked || !iType || (iType && iWidth && iHeight))
     {
-        // ***** STANDARD COLOR TEXTURE *****
-        if(pcNew!= nullptr)
-            delete pcNew;
-
         pcNew = new aiTexture();
         if (!iHeight || !iWidth)
         {

+ 42 - 0
code/MMDCpp14.h

@@ -0,0 +1,42 @@
+#pragma once
+
+#ifndef MMD_CPP14_H
+#define MMD_CPP14_H
+
+#include <cstddef>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+namespace mmd {
+    template<class T> struct _Unique_if {
+        typedef std::unique_ptr<T> _Single_object;
+    };
+
+    template<class T> struct _Unique_if<T[]> {
+        typedef std::unique_ptr<T[]> _Unknown_bound;
+    };
+
+    template<class T, size_t N> struct _Unique_if<T[N]> {
+        typedef void _Known_bound;
+    };
+
+    template<class T, class... Args>
+        typename _Unique_if<T>::_Single_object
+        make_unique(Args&&... args) {
+            return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+        }
+
+    template<class T>
+        typename _Unique_if<T>::_Unknown_bound
+        make_unique(size_t n) {
+            typedef typename std::remove_extent<T>::type U;
+            return std::unique_ptr<T>(new U[n]());
+        }
+
+    template<class T, class... Args>
+        typename _Unique_if<T>::_Known_bound
+        make_unique(Args&&...) = delete;
+}
+
+#endif

+ 370 - 0
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 <assimp/DefaultIOSystem.h>
+#include <assimp/Importer.hpp>
+#include <assimp/ai_assert.h>
+#include <assimp/scene.h>
+#include <fstream>
+#include <iomanip>
+#include <memory>
+
+static const aiImporterDesc desc = {"MMD Importer",
+                                    "",
+                                    "",
+                                    "surfaces supported?",
+                                    aiImporterFlags_SupportTextFlavour,
+                                    0,
+                                    0,
+                                    0,
+                                    0,
+                                    "pmx"};
+
+namespace Assimp {
+
+using namespace std;
+
+// ------------------------------------------------------------------------------------------------
+//  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<int, vector<aiVertexWeight>> 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<pmx::PmxVertexSkinningBDEF1 *>(v->skinning.get());
+    const auto vsBDEF2_ptr =
+        dynamic_cast<pmx::PmxVertexSkinningBDEF2 *>(v->skinning.get());
+    const auto vsBDEF4_ptr =
+        dynamic_cast<pmx::PmxVertexSkinningBDEF4 *>(v->skinning.get());
+    const auto vsSDEF_ptr =
+        dynamic_cast<pmx::PmxVertexSkinningSDEF *>(v->skinning.get());
+    switch (v->skinning_type) {
+    case pmx::PmxVertexSkinningType::BDEF1:
+      bone_vertex_map[vsBDEF1_ptr->bone_index].push_back(
+          aiVertexWeight(index, 1.0));
+      break;
+    case pmx::PmxVertexSkinningType::BDEF2:
+      bone_vertex_map[vsBDEF2_ptr->bone_index1].push_back(
+          aiVertexWeight(index, vsBDEF2_ptr->bone_weight));
+      bone_vertex_map[vsBDEF2_ptr->bone_index2].push_back(
+          aiVertexWeight(index, 1.0 - vsBDEF2_ptr->bone_weight));
+      break;
+    case pmx::PmxVertexSkinningType::BDEF4:
+      bone_vertex_map[vsBDEF4_ptr->bone_index1].push_back(
+          aiVertexWeight(index, vsBDEF4_ptr->bone_weight1));
+      bone_vertex_map[vsBDEF4_ptr->bone_index2].push_back(
+          aiVertexWeight(index, vsBDEF4_ptr->bone_weight2));
+      bone_vertex_map[vsBDEF4_ptr->bone_index3].push_back(
+          aiVertexWeight(index, vsBDEF4_ptr->bone_weight3));
+      bone_vertex_map[vsBDEF4_ptr->bone_index4].push_back(
+          aiVertexWeight(index, vsBDEF4_ptr->bone_weight4));
+      break;
+    case pmx::PmxVertexSkinningType::SDEF: // 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<pmx::PmxVertexSkinningQDEF *>(v->skinning.get());
+      bone_vertex_map[vsQDEF_ptr->bone_index1].push_back(
+          aiVertexWeight(index, vsQDEF_ptr->bone_weight1));
+      bone_vertex_map[vsQDEF_ptr->bone_index2].push_back(
+          aiVertexWeight(index, vsQDEF_ptr->bone_weight2));
+      bone_vertex_map[vsQDEF_ptr->bone_index3].push_back(
+          aiVertexWeight(index, vsQDEF_ptr->bone_weight3));
+      bone_vertex_map[vsQDEF_ptr->bone_index4].push_back(
+          aiVertexWeight(index, vsQDEF_ptr->bone_weight4));
+      break;
+    }
+  }
+
+  // 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<aiVertexWeight>));
+    }
+    bone_ptr_ptr[ii] = pBone;
+  }
+
+  return pMesh;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiMaterial *MMDImporter::CreateMaterial(const pmx::PmxMaterial *pMat,
+                                        const pmx::PmxModel *pModel) {
+  aiMaterial *mat = new aiMaterial();
+  aiString name(pMat->material_english_name);
+  mat->AddProperty(&name, AI_MATKEY_NAME);
+
+  aiColor3D diffuse(pMat->diffuse[0], pMat->diffuse[1], pMat->diffuse[2]);
+  mat->AddProperty(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
+  aiColor3D specular(pMat->specular[0], pMat->specular[1], pMat->specular[2]);
+  mat->AddProperty(&specular, 1, AI_MATKEY_COLOR_SPECULAR);
+  aiColor3D ambient(pMat->ambient[0], pMat->ambient[1], pMat->ambient[2]);
+  mat->AddProperty(&ambient, 1, AI_MATKEY_COLOR_AMBIENT);
+
+  float opacity = pMat->diffuse[3];
+  mat->AddProperty(&opacity, 1, AI_MATKEY_OPACITY);
+  float shininess = pMat->specularlity;
+  mat->AddProperty(&shininess, 1, AI_MATKEY_SHININESS_STRENGTH);
+
+  aiString texture_path(pModel->textures[pMat->diffuse_texture_index]);
+  mat->AddProperty(&texture_path, AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0));
+  int mapping_uvwsrc = 0;
+  mat->AddProperty(&mapping_uvwsrc, 1,
+                   AI_MATKEY_UVWSRC(aiTextureType_DIFFUSE, 0));
+
+  return mat;
+}
+
+// ------------------------------------------------------------------------------------------------
+
+} // Namespace Assimp
+
+#endif // !! ASSIMP_BUILD_NO_MMD_IMPORTER

+ 96 - 0
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 <assimp/material.h>
+#include <vector>
+
+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<char> m_Buffer;
+    //! Absolute pathname of model in file system
+    std::string m_strAbsPath;
+};
+
+// ------------------------------------------------------------------------------------------------
+
+} // Namespace Assimp
+
+#endif

+ 630 - 0
code/MMDPmdParser.h

@@ -0,0 +1,630 @@
+#pragma once
+
+#include <vector>
+#include <string>
+#include <memory>
+#include <iostream>
+#include <fstream>
+#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<uint16_t> 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<PmdFaceVertex> 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<PmdVertex> vertices;
+		std::vector<uint16_t> indices;
+		std::vector<PmdMaterial> materials;
+		std::vector<PmdBone> bones;
+		std::vector<PmdIk> iks;
+		std::vector<PmdFace> faces;
+		std::vector<uint16_t> faces_indices;
+		std::vector<PmdBoneDispName> bone_disp_name;
+		std::vector<PmdBoneDisp> bone_disp;
+		std::vector<std::string> toon_filenames;
+		std::vector<PmdRigidBody> rigid_bodies;
+		std::vector<PmdConstraint> constraints;
+
+		static std::unique_ptr<PmdModel> 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<PmdModel> LoadFromStream(std::ifstream *stream)
+		{
+			auto result = mmd::make_unique<PmdModel>();
+			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;
+		}
+	};
+}

+ 608 - 0
code/MMDPmxParser.cpp

@@ -0,0 +1,608 @@
+#include <utility>
+#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<char> 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<PmxVertexSkinningBDEF1>();
+			break;
+		case PmxVertexSkinningType::BDEF2:
+			this->skinning = mmd::make_unique<PmxVertexSkinningBDEF2>();
+			break;
+		case PmxVertexSkinningType::BDEF4:
+			this->skinning = mmd::make_unique<PmxVertexSkinningBDEF4>();
+			break;
+		case PmxVertexSkinningType::SDEF:
+			this->skinning = mmd::make_unique<PmxVertexSkinningSDEF>();
+			break;
+		case PmxVertexSkinningType::QDEF:
+			this->skinning = mmd::make_unique<PmxVertexSkinningQDEF>();
+			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<PmxIkLink []>(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<PmxMorphGroupOffset []>(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<PmxMorphVertexOffset []>(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<PmxMorphBoneOffset []>(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<PmxMorphMaterialOffset []>(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<PmxMorphUVOffset []>(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<PmxFrameElement []>(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<PmxVertex []>(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<int []>(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<std::string []>(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<PmxMaterial []>(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<PmxBone []>(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<PmxMorph []>(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<PmxFrame []>(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<PmxRigidBody []>(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<PmxJoint []>(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<PmxSoftBody []>(this->soft_body_count);
+		//	for (int i = 0; i < this->soft_body_count; i++)
+		//	{
+		//		this->soft_bodies[i].Read(stream, &setting);
+		//	}
+		//}
+	}
+
+	//std::unique_ptr<PmxModel> 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<PmxModel> ReadFromStream(std::istream *stream)
+	//{
+	//	auto pmx = mmd::make_unique<PmxModel>();
+	//	pmx->Read(stream);
+	//	return pmx;
+	//}
+}

+ 860 - 0
code/MMDPmxParser.h

@@ -0,0 +1,860 @@
+#pragma once
+
+#include <vector>
+#include <string>
+#include <iostream>
+#include <fstream>
+#include <memory>
+#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<PmxVertexSkinning> 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<PmxIkLink []> 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<PmxMorphVertexOffset []> vertex_offsets;
+		/// UVモーフ配列
+		std::unique_ptr<PmxMorphUVOffset []> uv_offsets;
+		/// ボーンモーフ配列
+		std::unique_ptr<PmxMorphBoneOffset []> bone_offsets;
+		/// マテリアルモーフ配列
+		std::unique_ptr<PmxMorphMaterialOffset []> material_offsets;
+		/// グループモーフ配列
+		std::unique_ptr<PmxMorphGroupOffset []> group_offsets;
+		/// フリップモーフ配列
+		std::unique_ptr<PmxMorphFlipOffset []> flip_offsets;
+		/// インパルスモーフ配列
+		std::unique_ptr<PmxMorphImplusOffset []> 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<PmxFrameElement []> 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<PmxAncherRigidBody []> anchers;
+		int pin_vertex_count;
+		std::unique_ptr<int []> 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<PmxVertex []> vertices;
+		/// インデックス数
+		int index_count;
+		/// インデックス配列
+		std::unique_ptr<int []> indices;
+		/// テクスチャ数
+		int texture_count;
+		/// テクスチャ配列
+		std::unique_ptr< std::string []> textures;
+		/// マテリアル数
+		int material_count;
+		/// マテリアル
+		std::unique_ptr<PmxMaterial []> materials;
+		/// ボーン数
+		int bone_count;
+		/// ボーン配列
+		std::unique_ptr<PmxBone []> bones;
+		/// モーフ数
+		int morph_count;
+		/// モーフ配列
+		std::unique_ptr<PmxMorph []> morphs;
+		/// 表示枠数
+		int frame_count;
+		/// 表示枠配列
+		std::unique_ptr<PmxFrame [] > frames;
+		/// 剛体数
+		int rigid_body_count;
+		/// 剛体配列
+		std::unique_ptr<PmxRigidBody []> rigid_bodies;
+		/// ジョイント数
+		int joint_count;
+		/// ジョイント配列
+		std::unique_ptr<PmxJoint []> joints;
+		/// ソフトボディ数
+		int soft_body_count;
+		/// ソフトボディ配列
+		std::unique_ptr<PmxSoftBody []> soft_bodies;
+		/// モデル初期化
+		void Init();
+		/// モデル読み込み
+		void Read(std::istream *stream);
+		///// ファイルからモデルの読み込み
+		//static std::unique_ptr<PmxModel> ReadFromFile(const char *filename);
+		///// 入力ストリームからモデルの読み込み
+		//static std::unique_ptr<PmxModel> ReadFromStream(std::istream *stream);
+	};
+}

+ 367 - 0
code/MMDVmdParser.h

@@ -0,0 +1,367 @@
+#pragma once
+
+#include <vector>
+#include <string>
+#include <memory>
+#include <iostream>
+#include <fstream>
+#include <ostream>
+#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<VmdIkEnable> 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<int>(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<VmdBoneFrame> bone_frames;
+		/// 表情フレーム
+		std::vector<VmdFaceFrame> face_frames;
+		/// カメラフレーム
+		std::vector<VmdCameraFrame> camera_frames;
+		/// ライトフレーム
+		std::vector<VmdLightFrame> light_frames;
+		/// IKフレーム
+		std::vector<VmdIkFrame> ik_frames;
+
+		static std::unique_ptr<VmdMotion> LoadFromFile(char const *filename)
+		{
+			std::ifstream stream(filename, std::ios::binary);
+			auto result = LoadFromStream(&stream);
+			stream.close();
+			return result;
+		}
+
+		static std::unique_ptr<VmdMotion> LoadFromStream(std::ifstream *stream)
+		{
+
+			char buffer[30];
+			auto result = mmd::make_unique<VmdMotion>();
+
+			// 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<int>(bone_frames.size());
+			stream->write(reinterpret_cast<const char*>(&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<int>(face_frames.size());
+			stream->write(reinterpret_cast<const char*>(&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<int>(camera_frames.size());
+			stream->write(reinterpret_cast<const char*>(&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<int>(light_frames.size());
+			stream->write(reinterpret_cast<const char*>(&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<const char*>(&self_shadow_num), sizeof(int));
+
+			// ik frames
+			const int ik_num = static_cast<int>(ik_frames.size());
+			stream->write(reinterpret_cast<const char*>(&ik_num), sizeof(int));
+			for (int i = 0; i < ik_num; i++)
+			{
+				ik_frames[i].Write(stream);
+			}
+
+			return true;
+		}
+	};
+}

+ 7 - 4
code/ObjExporter.cpp

@@ -122,14 +122,17 @@ std::string ObjExporter :: GetMaterialLibName()
 }
 
 // ------------------------------------------------------------------------------------------------
-std::string ObjExporter :: GetMaterialLibFileName()
-{
+std::string ObjExporter::GetMaterialLibFileName() {
+    // Remove existing .obj file extention so that the final material file name will be fileName.mtl and not fileName.obj.mtl
+    size_t lastdot = filename.find_last_of('.');
+    if (lastdot != std::string::npos)
+        return filename.substr(0, lastdot) + MaterialExt;
+
     return filename + MaterialExt;
 }
 
 // ------------------------------------------------------------------------------------------------
-void ObjExporter :: WriteHeader(std::ostringstream& out)
-{
+void ObjExporter :: WriteHeader(std::ostringstream& out) {
     out << "# File produced by Open Asset Import Library (http://www.assimp.sf.net)" << endl;
     out << "# (assimp v" << aiGetVersionMajor() << '.' << aiGetVersionMinor() << '.' << aiGetVersionRevision() << ")" << endl  << endl;
 }

+ 1 - 1
code/ObjExporter.h

@@ -105,7 +105,7 @@ private:
     void AddNode(const aiNode* nd, const aiMatrix4x4& mParent);
 
 private:
-    const std::string filename;
+    std::string filename;
     const aiScene* const pScene;
 
     std::vector<aiVector3D> vp, vn, vt;

+ 1 - 1
code/ObjFileMtlImporter.h

@@ -113,4 +113,4 @@ private:
 
 } // Namespace Assimp
 
-#endif
+#endif // OBJFILEMTLIMPORTER_H_INC

+ 25 - 14
code/ObjFileParser.cpp

@@ -57,8 +57,17 @@ namespace Assimp {
 
 const std::string ObjFileParser::DEFAULT_MATERIAL = AI_DEFAULT_MATERIAL_NAME;
 
-// -------------------------------------------------------------------
-//  Constructor with loaded data and directories.
+ObjFileParser::ObjFileParser()
+: m_DataIt()
+, m_DataItEnd()
+, m_pModel( NULL )
+, m_uiLine( 0 )
+, m_pIO( nullptr )
+, m_progress( nullptr )
+, m_originalObjFileName( "" ) {
+    // empty
+}
+
 ObjFileParser::ObjFileParser( IOStreamBuffer<char> &streamBuffer, const std::string &modelName,
                               IOSystem *io, ProgressHandler* progress,
                               const std::string &originalObjFileName) :
@@ -86,18 +95,20 @@ ObjFileParser::ObjFileParser( IOStreamBuffer<char> &streamBuffer, const std::str
     parseFile( streamBuffer );
 }
 
-// -------------------------------------------------------------------
-//  Destructor
 ObjFileParser::~ObjFileParser() {
     delete m_pModel;
     m_pModel = NULL;
 }
 
-// -------------------------------------------------------------------
-//  Returns a pointer to the model instance.
+void ObjFileParser::setBuffer( std::vector<char> &buffer ) {
+    m_DataIt = buffer.begin();
+    m_DataItEnd = buffer.end();
+}
+
 ObjFile::Model *ObjFileParser::GetModel() const {
     return m_pModel;
 }
+
 void ignoreNewLines(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer)
 {
     auto curPosition = buffer.begin();
@@ -119,8 +130,7 @@ void ignoreNewLines(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffe
         }
     } while (*curPosition!='\n');
 }
-// -------------------------------------------------------------------
-//  File parsing method.
+
 void ObjFileParser::parseFile( IOStreamBuffer<char> &streamBuffer ) {
     // only update every 100KB or it'll be too slow
     //const unsigned int updateProgressEveryBytes = 100 * 1024;
@@ -144,7 +154,7 @@ void ObjFileParser::parseFile( IOStreamBuffer<char> &streamBuffer ) {
             progressCounter++;
             m_progress->UpdateFileRead( progressOffset + processed * 2, progressTotal );
         }
-		ignoreNewLines(streamBuffer, buffer);
+		//ignoreNewLines(streamBuffer, buffer);
         // parse line
         switch (*m_DataIt) {
         case 'v': // Parse a vertex texture coordinate
@@ -243,11 +253,14 @@ pf_skip_line:
     }
 }
 
-// -------------------------------------------------------------------
-//  Copy the next word in a temporary buffer
 void ObjFileParser::copyNextWord(char *pBuffer, size_t length) {
     size_t index = 0;
     m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd);
+    if ( *m_DataIt == '\\' ) {
+        m_DataIt++;
+        m_DataIt++;
+        m_DataIt = getNextWord<DataArrayIt>( m_DataIt, m_DataItEnd );
+    }
     while( m_DataIt != m_DataItEnd && !IsSpaceOrNewLine( *m_DataIt ) ) {
         pBuffer[index] = *m_DataIt;
         index++;
@@ -264,7 +277,7 @@ void ObjFileParser::copyNextWord(char *pBuffer, size_t length) {
 size_t ObjFileParser::getNumComponentsInLine() {
     size_t numComponents( 0 );
     const char* tmp( &m_DataIt[0] );
-    while( !IsLineEnd( *tmp ) ) {
+    while( !IsLineEnd( *tmp ) ) {        
         if ( !SkipSpaces( &tmp ) ) {
             break;
         }
@@ -300,8 +313,6 @@ void ObjFileParser::getVector( std::vector<aiVector3D> &point3d_array ) {
     m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
 }
 
-// -------------------------------------------------------------------
-//  Get values for a new 3D vector instance
 void ObjFileParser::getVector3( std::vector<aiVector3D> &point3d_array ) {
     ai_real x, y, z;
     copyNextWord(m_buffer, Buffersize);

+ 10 - 6
code/ObjFileParser.h

@@ -65,7 +65,7 @@ class ProgressHandler;
 
 /// \class  ObjFileParser
 /// \brief  Parser for a obj waveform file
-class ObjFileParser {
+class ASSIMP_API ObjFileParser {
 public:
     static const size_t Buffersize = 4096;
     typedef std::vector<char> DataArray;
@@ -73,14 +73,18 @@ public:
     typedef std::vector<char>::const_iterator ConstDataArrayIt;
 
 public:
-    /// \brief  Constructor with data array.
-    ObjFileParser( IOStreamBuffer<char> &streamBuffer, const std::string &strModelName, IOSystem* io, ProgressHandler* progress, const std::string &originalObjFileName);
-    /// \brief  Destructor
+    /// @brief  The default constructor.
+    ObjFileParser();
+    /// @brief  Constructor with data array.
+    ObjFileParser( IOStreamBuffer<char> &streamBuffer, const std::string &modelName, IOSystem* io, ProgressHandler* progress, const std::string &originalObjFileName);
+    /// @brief  Destructor
     ~ObjFileParser();
-    /// \brief  Model getter.
+    /// @brief  If you want to load in-core data.
+    void setBuffer( std::vector<char> &buffer );
+    /// @brief  Model getter.
     ObjFile::Model *GetModel() const;
 
-private:
+protected:
     /// Parse the loaded file
     void parseFile( IOStreamBuffer<char> &streamBuffer );
     /// Method to copy the new delimited word in the current line.

+ 4 - 2
code/ObjTools.h

@@ -79,8 +79,10 @@ inline Char_T getNextWord( Char_T pBuffer, Char_T pEnd )
 {
     while ( !isEndOfBuffer( pBuffer, pEnd ) )
     {
-        if( !IsSpaceOrNewLine( *pBuffer ) || IsLineEnd( *pBuffer ) )
-            break;
+        if ( !IsSpaceOrNewLine( *pBuffer ) || IsLineEnd( *pBuffer ) ) {
+            //if ( *pBuffer != '\\' )
+                break;
+        }
         pBuffer++;
     }
     return pBuffer;

+ 40 - 3
code/OpenGEXImporter.cpp

@@ -196,7 +196,6 @@ namespace Grammar {
 
         return NoneType;
     }
-
 } // Namespace Grammar
 
 namespace Assimp {
@@ -523,6 +522,10 @@ void OpenGEXImporter::handleObjectRefNode( DDLNode *node, aiScene *pScene ) {
         if ( !objRefNames.empty() ) {
             m_unresolvedRefStack.push_back( new RefInfo( m_currentNode, RefInfo::MeshRef, objRefNames ) );
         }
+    } else if ( m_tokenType == Grammar::LightNodeToken ) {
+        // TODO!
+    } else if ( m_tokenType == Grammar::CameraNodeToken ) {
+        // TODO!
     }
 }
 
@@ -602,6 +605,13 @@ void OpenGEXImporter::handleCameraObject( ODDLParser::DDLNode *node, aiScene *pS
 
 //------------------------------------------------------------------------------------------------
 void OpenGEXImporter::handleLightObject( ODDLParser::DDLNode *node, aiScene *pScene ) {
+    aiLight *light( new aiLight );
+    m_lightCache.push_back( light );
+    std::string objName = node->getName();
+    if ( !objName.empty() ) {
+        light->mName.Set( objName );
+    }
+    m_currentLight = light;
 
     Property *prop( node->findPropertyByName( "type" ) );
     if ( nullptr != prop ) {
@@ -917,7 +927,7 @@ void OpenGEXImporter::handleIndexArrayNode( ODDLParser::DDLNode *node, aiScene *
 }
 
 //------------------------------------------------------------------------------------------------
-static void getColorRGB( aiColor3D *pColor, DataArrayList *colList ) {
+static void getColorRGB3( aiColor3D *pColor, DataArrayList *colList ) {
     if( nullptr == pColor || nullptr == colList ) {
         return;
     }
@@ -931,6 +941,23 @@ static void getColorRGB( aiColor3D *pColor, DataArrayList *colList ) {
     pColor->b = val->getFloat();
 }
 
+//------------------------------------------------------------------------------------------------
+static void getColorRGB4( aiColor4D *pColor, DataArrayList *colList ) {
+    if ( nullptr == pColor || nullptr == colList ) {
+        return;
+    }
+
+    ai_assert( 4 == colList->m_numItems );
+    Value *val( colList->m_dataList );
+    pColor->r = val->getFloat();
+    val = val->getNext();
+    pColor->g = val->getFloat();
+    val = val->getNext();
+    pColor->b = val->getFloat();
+    val = val->getNext();
+    pColor->a = val->getFloat();
+}
+
 //------------------------------------------------------------------------------------------------
 enum ColorType {
     NoneColor = 0,
@@ -981,7 +1008,17 @@ void OpenGEXImporter::handleColorNode( ODDLParser::DDLNode *node, aiScene *pScen
                 return;
             }
             aiColor3D col;
-            getColorRGB( &col, colList );
+            if ( 3 == colList->m_numItems ) {
+                aiColor3D col3;
+                getColorRGB3( &col3, colList );
+                col = col3;
+            } else {
+                aiColor4D col4;
+                getColorRGB4( &col4, colList );
+                col.r = col4.r;
+                col.g = col4.g;
+                col.b = col4.b;
+            }
             const ColorType colType( getColorType( prop->m_key ) );
             if( DiffuseColor == colType ) {
                 m_currentMaterial->AddProperty( &col, 1, AI_MATKEY_COLOR_DIFFUSE );

+ 0 - 1
code/OpenGEXImporter.h

@@ -132,7 +132,6 @@ protected:
     void copyMeshes( aiScene *pScene );
     void copyCameras( aiScene *pScene );
     void copyLights( aiScene *pScene );
-
     void resolveReferences();
     void pushNode( aiNode *node, aiScene *pScene );
     aiNode *popNode();

+ 1 - 1
code/RemoveVCProcess.cpp

@@ -306,7 +306,7 @@ bool RemoveVCProcess::ProcessMesh(aiMesh* pMesh)
         if (!pMesh->mColors[i])break;
         if (configDeleteFlags & aiComponent_COLORSn(i) || b)
         {
-            delete pMesh->mColors[i];
+            delete [] pMesh->mColors[i];
             pMesh->mColors[i] = NULL;
             ret = true;
 

+ 2 - 2
code/X3DImporter.cpp

@@ -241,7 +241,7 @@ void X3DImporter::XML_CheckNode_MustBeEmpty()
 
 void X3DImporter::XML_CheckNode_SkipUnsupported(const std::string& pParentNodeName)
 {
-    const size_t Uns_Skip_Len = 189;
+    static const size_t Uns_Skip_Len = 190;
     const char* Uns_Skip[ Uns_Skip_Len ] = {
 	    // CAD geometry component
 	    "CADAssembly", "CADFace", "CADLayer", "CADPart", "IndexedQuadSet", "QuadSet",
@@ -268,7 +268,7 @@ void X3DImporter::XML_CheckNode_SkipUnsupported(const std::string& pParentNodeNa
 	    "PositionInterpolator", "PositionInterpolator2D", "ScalarInterpolator", "SplinePositionInterpolator", "SplinePositionInterpolator2D",
 	    "SplineScalarInterpolator", "SquadOrientationInterpolator",
 	    // Key device sensor component
-	    "KeySensor", "StringSensor"
+	    "KeySensor", "StringSensor",
 	    // Layering component
 	    "Layer", "LayerSet", "Viewport",
 	    // Layout component

+ 3 - 0
code/X3DImporter_Postprocess.cpp

@@ -504,6 +504,9 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 		// copy additional information from children
 		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
 		{
+			if ( nullptr == *pMesh ) {
+				break;
+			}
 			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
 				MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value,tnemesh.ColorPerVertex);
 			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)

+ 11 - 11
doc/Preamble.txt

@@ -3,12 +3,12 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2012, assimp team
+Copyright (c) 2006-2017, 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 
+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
@@ -25,16 +25,16 @@ conditions are met:
   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 
+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 
+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 
+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 
+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.
 ---------------------------------------------------------------------------
-*/
+*/

+ 9 - 20
doc/dox.h

@@ -453,7 +453,7 @@ by calling it as a singleton with the requested logging-type. To see how this wo
 using namespace Assimp;
 
 // Create a logger instance
-DefaultLogger::create("",Logger::VERBOSE);
+DefaultLogger::create("", Logger::VERBOSE);
 
 // Now I am ready for logging my stuff
 DefaultLogger::get()->info("this is my info-call");
@@ -472,22 +472,9 @@ Just derivate your own logger from the abstract base class LogStream and overwri
 
 @code
 // Example stream
-class myStream :
-	public LogStream
+class myStream : public LogStream
 {
 public:
-	// Constructor
-	myStream()
-	{
-		// empty
-	}
-
-	// Destructor
-	~myStream()
-	{
-		// empty
-	}
-
 	// Write womethink using your own functionality
 	void write(const char* message)
 	{
@@ -496,10 +483,10 @@ public:
 };
 
 // Select the kinds of messages you want to receive on this log stream
-const unsigned int severity = Logger::DEBUGGING|Logger::INFO|Logger::ERR|Logger::WARN;
+const unsigned int severity = Logger::Debugging|Logger::Info|Logger::Err|Logger::Warn;
 
 // Attaching it to the default logger
-Assimp::DefaultLogger::get()->attachStream( new myStream(), severity );
+Assimp::DefaultLogger::get()->attachStream( new myStream, severity );
 
 @endcode
 
@@ -512,10 +499,10 @@ flag set:
 @code
 
 unsigned int severity = 0;
-severity |= Logger::DEBUGGING;
+severity |= Logger::Debugging;
 
 // Detach debug messages from you self defined stream
-Assimp::DefaultLogger::get()->attachStream( new myStream(), severity );
+Assimp::DefaultLogger::get()->attachStream( new myStream, severity );
 
 @endcode
 
@@ -743,6 +730,8 @@ need them at all.
 Normally textures used by assets are stored in separate files, however,
 there are file formats embedding their textures directly into the model file.
 Such textures are loaded into an aiTexture structure.
+For embedded textures, the value of `AI_MATKEY_TEXTURE(textureType, index)` will be `*<index>` where
+`<index>` is the index of the texture in aiScene::mTextures.
 <br>
 There are two cases:
 <br>
@@ -930,7 +919,7 @@ All material key constants start with 'AI_MATKEY' (it's an ugly macro for histor
     <td><tt>TEXTURE(t,n)</tt></td>
     <td>aiString</td>
     <td>n/a</td>
-	<td>Defines the path to the n'th texture on the stack 't', where 'n' is any value >= 0 and 't' is one of the #aiTextureType enumerated values.</td>
+	<td>Defines the path of the n'th texture on the stack 't', where 'n' is any value >= 0 and 't' is one of the #aiTextureType enumerated values. Either a filepath or `*<index>`, where `<index>` is the index of an embedded texture in aiScene::mTextures.</td>
 	<td>See the 'Textures' section above.</td>
   </tr>
 

+ 3 - 1
test/CMakeLists.txt

@@ -38,7 +38,6 @@
 #----------------------------------------------------------------------
 cmake_minimum_required( VERSION 2.6 )
 
-#INCLUDE( AddGTest )
 include( CTest )
 enable_testing()
 
@@ -107,11 +106,14 @@ SET( TEST_SRCS
   unit/utSIBImporter.cpp
   unit/utObjImportExport.cpp
   unit/utObjTools.cpp
+  unit/utOpenGEXImportExport.cpp
   unit/utPretransformVertices.cpp
   unit/utPLYImportExport.cpp
+  unit/utPMXImporter.cpp
   unit/utRemoveComments.cpp
   unit/utRemoveComponent.cpp
   unit/utRemoveRedundantMaterials.cpp
+  unit/utRemoveVCProcess.cpp
   unit/utScenePreprocessor.cpp
   unit/utSharedPPData.cpp
   unit/utStringUtils.cpp

BIN
test/models-nonbsd/MMD/Alicia_blade.pmx


+ 49 - 0
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 )を登録するよう努めるものとします。
+
+
+◯禁止事項
+* 第三者の知的財産権その他一切の権利及び名誉を侵害しないこと。
+* 当社(当社が提供するサービス等を含む)及び当社キャラクターの名誉・品位傷つける行為をしないこと。
+* 公序良俗に反する行為や目的、暴力的な表現、反社会的な行為や目的、特定の信条や宗教、政治的発言のため利用しないこと。
+* 当社の公式商品であるかのような誤解を招く利用をしないこと。
+* その他、当社が不適切と判断する行為に利用しないこと。
+
+
+◯ クレジット
+企画: 株式会社ドワンゴ
+キャラクターデザイン: 黒星紅白
+モデリング: 雨刻

+ 21 - 0
test/models/OpenGEX/light_issue1262.ogex

@@ -0,0 +1,21 @@
+LightObject (type = "infinite") {
+    Param (attrib = "intensity") {
+        float { 3.0 }
+    }
+
+    Color (attrib = "light") {
+        float[3] { { 0.7, 1.0, 0.1 } }
+    }
+}
+
+LightObject (type = "point") {
+    Param (attrib = "intensity") {
+        float { 0.5 }
+    }
+}
+
+LightObject (type = "spot") {
+    Color (attrib = "light") {
+        float[4] { { 0.1, 0.0, 0.1, 1.0 } }
+    }
+}

+ 29 - 1
test/unit/utObjTools.cpp

@@ -41,6 +41,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 #include "UnitTestPCH.h"
 #include "ObjTools.h"
+#include "ObjFileParser.h"
 
 using namespace ::Assimp;
 
@@ -48,6 +49,15 @@ class utObjTools : public ::testing::Test {
     // empty
 };
 
+class TestObjFileParser : public ObjFileParser {
+public:
+    TestObjFileParser() : ObjFileParser(){}
+    ~TestObjFileParser() {}
+    void testCopyNextWord( char *pBuffer, size_t length ) {
+        copyNextWord( pBuffer, length );
+    }
+
+};
 TEST_F( utObjTools, skipDataLine_OneLine_Success ) {
     std::vector<char> buffer;
     std::string data( "v -0.5 -0.5 0.5\nend" );
@@ -60,6 +70,24 @@ TEST_F( utObjTools, skipDataLine_OneLine_Success ) {
 }
 
 TEST_F( utObjTools, skipDataLine_TwoLines_Success ) {
-    std::string data( "vn - 2.061493116917992e-15 - 0.9009688496589661 \ \n- 0.4338837265968323 " );
+    TestObjFileParser test_parser;
+    std::string data( "vn -2.061493116917992e-15 -0.9009688496589661 \\n-0.4338837265968323" );
+    std::vector<char> buffer;
+    buffer.resize( data.size() );
+    ::memcpy( &buffer[ 0 ], &data[ 0 ], data.size() );
+    test_parser.setBuffer( buffer );
+    static const size_t Size = 4096UL;
+    char data_buffer[ Size ];
+    
+    test_parser.testCopyNextWord( data_buffer, Size );
+    EXPECT_EQ( 0, strncmp( data_buffer, "vn", 2 ) );
+
+    test_parser.testCopyNextWord( data_buffer, Size );
+    EXPECT_EQ( data_buffer[0], '-' );
+
+    test_parser.testCopyNextWord( data_buffer, Size );
+    EXPECT_EQ( data_buffer[0], '-' );
 
+    test_parser.testCopyNextWord( data_buffer, Size );
+    EXPECT_EQ( data_buffer[ 0 ], '-' );
 }

+ 70 - 0
test/unit/utOpenGEXImportExport.cpp

@@ -0,0 +1,70 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2017, 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 "AbstractImportExportBase.h"
+
+#include <assimp/Importer.hpp>
+
+using namespace Assimp;
+
+
+class utOpenGEXImportExport : public AbstractImportExportBase {
+public:
+    virtual bool importerTest() {
+        Assimp::Importer importer;
+        const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OpenGEX/Example.ogex", 0 );
+        return nullptr != scene;
+
+        return true;
+    }
+};
+
+TEST_F( utOpenGEXImportExport, importLWSFromFileTest ) {
+    EXPECT_TRUE( importerTest() );
+}
+
+TEST_F( utOpenGEXImportExport, Importissue1262_NoCrash ) {
+    Assimp::Importer importer;
+    const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OpenGEX/light_issue1262.ogex", 0 );
+
+}

+ 62 - 0
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 <assimp/Importer.hpp>
+
+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() );
+}

+ 75 - 0
test/unit/utRemoveVCProcess.cpp

@@ -0,0 +1,75 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2017, 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 "code/RemoveVCProcess.h"
+#include <assimp/mesh.h>
+#include <assimp/scene.h>
+
+using namespace ::Assimp;
+
+class utRevmoveVCProcess : public ::testing::Test {
+    // empty
+};
+
+TEST_F( utRevmoveVCProcess, createTest ) {
+    bool ok = true;
+    try {
+        RemoveVCProcess *process = new RemoveVCProcess;
+        delete process;
+    } catch ( ... ) {
+        ok = false;
+    }
+    EXPECT_TRUE( ok );
+}
+
+TEST_F( utRevmoveVCProcess, issue1266_ProcessMeshTest_NoCrash ) {
+    aiScene *scene = new aiScene;
+    scene->mNumMeshes = 1;
+    scene->mMeshes = new aiMesh*[ 1 ];
+
+    aiMesh *mesh = new aiMesh;
+    mesh->mNumVertices = 1;
+    mesh->mColors[ 0 ] = new aiColor4D[ 2 ];
+    scene->mMeshes[ 0 ] = mesh;
+    RemoveVCProcess *process = new RemoveVCProcess;
+    process->Execute( scene );
+}

+ 1 - 1
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)
 

+ 1 - 1
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);